<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { Modal, Upload } from 'ant-design-vue';
import type { UploadFile } from 'ant-design-vue';

import GalleryPickLink from '#/components/form/components/gallery-pick-link.vue';
import { uploadFile } from '#/api/core/upload';
import { uploadToOss } from '#/utils/oss-upload';
import {
  beforeImageUpload,
  resolveUploadFile,
} from '#/utils/use-image-upload-pending';
import { preferences } from '@vben/preferences';
import { Icon } from '#/components/icon';

interface FileItem {
  uid: string;
  name: string;
  status: 'uploading' | 'done' | 'error';
  url: string;
}

interface Props {
  modelValue: string[];
  multiple?: boolean;
  maxCount?: number;
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [],
  multiple: true,
  maxCount: 9,
});

const emit = defineEmits<{
  'update:modelValue': [value: string[]];
}>();

const fileList = ref<FileItem[]>([]);

const initFileList = () => {
  fileList.value = props.modelValue.map((url) => ({
    uid: url,
    name: url.split('/').pop() || 'file',
    status: 'done' as const,
    url,
  }));
};

initFileList();

watch(
  () => props.modelValue,
  (newVal) => {
    const currentUrls = fileList.value
      .filter((file) => file.status === 'done')
      .map((file) => file.url);
    if (JSON.stringify(newVal) !== JSON.stringify(currentUrls)) {
      fileList.value = newVal.map((url) => ({
        uid: url,
        name: url.split('/').pop() || 'file',
        status: 'done' as const,
        url,
      }));
    }
  },
  { deep: true },
);

const updateModelValue = () => {
  const urls = fileList.value
    .filter((file) => file.status === 'done' && file.url)
    .map((file) => file.url);
  emit('update:modelValue', urls);
};

const customRequest = async (options: any) => {
  const { file, onProgress, onSuccess, onError } = options;
  const actualFile = resolveUploadFile(file as File);

  const uploadingFile: FileItem = {
    uid: file.uid,
    name: file.name,
    status: 'uploading',
    url: '',
  };

  fileList.value = [...fileList.value, uploadingFile];

  try {
    const uploadMethod = preferences.app.uploadMethod || 'direct';
    const res =
      uploadMethod === 'direct'
        ? await uploadToOss({
            file: actualFile,
            onProgress: (percent) => {
              if (onProgress) {
                onProgress(percent);
              }
            },
          })
        : await uploadFile({ file: actualFile });

    fileList.value = fileList.value.map((item) =>
      item.uid === file.uid ? { ...item, status: 'done' as const, url: res.url } : item,
    );
    updateModelValue();
    onSuccess(res);
  } catch (error) {
    console.error('上传失败', error);
    fileList.value = fileList.value.map((item) =>
      item.uid === file.uid ? { ...item, status: 'error' as const } : item,
    );
    onError(error);
  }
};

const handleRemove = (file: UploadFile) => {
  fileList.value = fileList.value.filter((item) => item.uid !== file.uid);
  updateModelValue();
};

const previewVisible = ref(false);
const previewImage = ref('');
const previewTitle = ref('');

function onGallerySelect(urls: string[]) {
  const remain = props.maxCount - fileList.value.length;
  const picked = props.multiple ? urls.slice(0, remain) : urls.slice(0, 1);
  for (const url of picked) {
    if (fileList.value.some((f) => f.url === url)) {
      continue;
    }
    fileList.value = [
      ...fileList.value,
      {
        uid: url,
        name: url.split('/').pop() || 'file',
        status: 'done' as const,
        url,
      },
    ];
  }
  updateModelValue();
}

const handlePreview = async (file: UploadFile) => {
  previewImage.value = file.url || '';
  previewVisible.value = true;
  previewTitle.value = file.name || file.url?.split('/').pop() || '';
};

const showUploadButton = computed(() =>
  props.multiple ? fileList.value.length < props.maxCount : fileList.value.length === 0,
);

const galleryRemainCount = computed(() => Math.max(props.maxCount - fileList.value.length, 0));
</script>

<template>
  <div class="upload-image-wrap">
    <Upload
      :file-list="fileList"
      :before-upload="beforeImageUpload"
      :custom-request="customRequest"
      :multiple="multiple"
      :max-count="maxCount"
      :show-upload-list="{
        showPreviewIcon: true,
        showRemoveIcon: true,
      }"
      list-type="picture-card"
      accept="image/*"
      @remove="handleRemove"
      @preview="handlePreview"
    >
      <div v-if="showUploadButton" class="upload-button">
        <Icon icon="ant-design:plus-outlined" />
        <div class="ant-upload-text">上传图片</div>
      </div>
    </Upload>

    <GalleryPickLink
      v-if="showUploadButton"
      :multiple="multiple"
      :max-count="galleryRemainCount"
      @select="onGallerySelect"
    />

    <Modal v-model:visible="previewVisible" :title="previewTitle" footer="" width="60%">
      <img alt="预览图片" style="width: 100%" :src="previewImage" />
    </Modal>
  </div>
</template>

<style lang="less" scoped>
.upload-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  cursor: pointer;
}
</style>
