<script setup lang="ts">
/**
 * 多图上传（picture-card）+ 下方「开始监听粘贴」支持截图/图片粘贴
 */
import { computed, ref, watch } from 'vue';
import { Button, Modal, Upload, message } 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 { usePasteUploadListen } from '#/utils/use-paste-upload-listen';
import { preferences } from '@vben/preferences';
import { Icon } from '#/components/icon';

defineOptions({ name: 'UploadImage' });

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

interface Props {
  modelValue: string[];
  multiple?: boolean;
  maxCount?: number;
  /**
   * 素材库可选类型（透传 GalleryPickLink）
   * xk_file.type：0=图片 1=视频，默认仅图片
   */
  acceptTypes?: number[];
}

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

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

const IMAGE_ACCEPT = 'image/*,.jpg,.jpeg,.png,.gif,.webp,.bmp';

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);
};

/**
 * 上传单个图片文件（点击上传与粘贴共用）
 */
async function uploadOneImage(file: File) {
  const remain = props.maxCount - fileList.value.filter((f) => f.status === 'done').length;
  if (remain <= 0) {
    message.warning(`最多上传 ${props.maxCount} 张图片`);
    return;
  }
  const actualFile = resolveUploadFile(file);
  const uid = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
  const uploadingFile: FileItem = {
    uid,
    name: actualFile.name || file.name || 'paste.png',
    status: 'uploading',
    url: '',
  };
  fileList.value = [...fileList.value, uploadingFile];
  try {
    const uploadMethod = preferences.app.uploadMethod || 'direct';
    const res =
      uploadMethod === 'direct'
        ? await uploadToOss({ file: actualFile })
        : await uploadFile({ file: actualFile });
    fileList.value = fileList.value.map((item) =>
      item.uid === uid ? { ...item, status: 'done' as const, url: res.url } : item,
    );
    updateModelValue();
  } catch (error) {
    console.error('上传失败', error);
    fileList.value = fileList.value.filter((item) => item.uid !== uid);
    message.error('上传失败');
  }
}

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));

const { listening: pasteListening, toggle: togglePaste } = usePasteUploadListen({
  accept: IMAGE_ACCEPT,
  maxSize: 20 * 1024 * 1024,
  onFiles: async (files) => {
    for (const file of files) {
      const doneCount = fileList.value.filter((f) => f.status === 'done').length;
      if (doneCount >= props.maxCount) {
        message.warning(`最多上传 ${props.maxCount} 张图片`);
        break;
      }
      await uploadOneImage(file);
    }
  },
});

function onTogglePaste() {
  togglePaste();
  if (pasteListening.value) {
    message.success('已开启粘贴监听，可 Ctrl+V 粘贴图片/截图');
  } else {
    message.info('已停止粘贴监听');
  }
}
</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"
      :accept-types="acceptTypes"
      @select="onGallerySelect"
    />

    <!-- 粘贴监听放在上传区下方外侧 -->
    <div class="mt-2">
      <Button
        size="small"
        :type="pasteListening ? 'primary' : 'default'"
        :danger="pasteListening"
        @click="onTogglePaste"
      >
        <Icon
          :icon="pasteListening ? 'ant-design:stop-outlined' : 'ant-design:snippets-outlined'"
          class="mr-1"
        />
        {{ pasteListening ? '停止监听粘贴' : '开始监听粘贴' }}
      </Button>
      <span v-if="pasteListening" class="ml-2 text-xs text-orange-500">
        监听中：Ctrl+V 粘贴图片
      </span>
    </div>

    <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>
