<script setup lang="ts">
/**
 * OSS 文件上传（单文件 URL 字符串）
 * 可选：从素材库选择（acceptTypes 过滤，如文件=6、语音=3）
 */
import { computed, ref, watch } from 'vue';

import { preferences } from '@vben/preferences';
import { Button, Upload } from 'ant-design-vue';

import GalleryPickLink from '#/components/form/components/gallery-pick-link.vue';
import { Icon } from '#/components/icon';
import { uploadFile } from '#/api/core/upload';
import { uploadToOss } from '#/utils/oss-upload';

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

interface Props {
  modelValue?: string;
  maxCount?: number;
  /** 素材库类型过滤，如 [6] 文件、[3] 语音 */
  acceptTypes?: number[];
  /** input accept，如 .pdf,.doc 或 audio/* */
  accept?: string;
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: '',
  maxCount: 1,
  acceptTypes: () => [6],
  accept: '.pdf,.doc,.docx,.zip,.rar,.txt,application/pdf,audio/*,*/*',
});

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

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

function syncFileListFromValue(url: string) {
  if (!url) {
    fileList.value = [];
    return;
  }
  fileList.value = [
    {
      uid: url,
      name: url.split('/').pop() || 'file',
      status: 'done',
      url,
    },
  ];
}

syncFileListFromValue(props.modelValue);

watch(
  () => props.modelValue,
  (newVal) => {
    const currentUrl =
      fileList.value.find((file) => file.status === 'done')?.url ?? '';
    if ((newVal ?? '') !== currentUrl) {
      syncFileListFromValue(newVal ?? '');
    }
  },
);

function updateModelValue() {
  const url =
    fileList.value.find((file) => file.status === 'done' && file.url)?.url ??
    '';
  emit('update:modelValue', url);
}

function beforeUpload(file: File) {
  const maxSize = 20 * 1024 * 1024;
  if (file.size > maxSize) {
    return Upload.LIST_IGNORE;
  }
  return true;
}

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

  fileList.value = [
    {
      uid: file.uid,
      name: file.name,
      status: 'uploading',
      url: '',
    },
  ];

  try {
    const uploadMethod = preferences.app.uploadMethod || 'direct';
    let res: { url: string };

    if (uploadMethod === 'direct') {
      res = await uploadToOss({
        file: file as File,
        onProgress: (percent) => {
          onProgress?.({ percent });
        },
      });
    } else {
      res = await uploadFile({
        file: file as File,
      });
    }

    fileList.value = [
      {
        uid: file.uid,
        name: file.name,
        status: 'done',
        url: res.url,
      },
    ];
    updateModelValue();
    onSuccess?.(res);
  } catch (error) {
    fileList.value = [
      {
        uid: file.uid,
        name: file.name,
        status: 'error',
        url: '',
      },
    ];
    onError?.(error);
  }
};

function handleRemove() {
  fileList.value = [];
  updateModelValue();
  return true;
}

function onGallerySelect(urls: string[]) {
  const url = urls[0] || '';
  if (!url) return;
  syncFileListFromValue(url);
  updateModelValue();
}

const showUploadButton = computed(
  () => fileList.value.filter((item) => item.status !== 'error').length === 0,
);
</script>

<template>
  <div class="upload-oss-file-wrap">
    <Upload
      :file-list="fileList"
      :before-upload="beforeUpload"
      :custom-request="customRequest"
      :max-count="maxCount"
      list-type="text"
      :accept="accept"
      @remove="handleRemove"
    >
      <Button v-if="showUploadButton">
        上传文件
        <template #icon>
          <Icon icon="ant-design:cloud-upload-outlined" />
        </template>
      </Button>
    </Upload>
    <GalleryPickLink
      v-if="showUploadButton"
      :max-count="1"
      :accept-types="acceptTypes"
      @select="onGallerySelect"
    />
  </div>
</template>

<style scoped>
.upload-oss-file-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}
</style>
