<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue';
import type { UploadCustomRequestOptions, UploadFileInfo } from 'naive-ui';
import { client, safeClient } from '@/service/api';
import { type UploadFetchOptions, uploadToS3 } from '@/utils/aws/s3';

defineOptions({ name: 'UploadS3' });

const props = withDefaults(
  defineProps<{
    modelValue?: string[];
    label?: string;
    placeholder?: string;
    accept?: string;
    multiple?: boolean;
    maxSize?: number; // MB
    maxFiles?: number;
    fetchOptions: Partial<UploadFetchOptions>;
  }>(),
  {
    modelValue: () => [],
    accept: '*/*',
    multiple: true,
    maxSize: 10,
    maxFiles: 5,
    placeholder: '',
    label: ''
  }
);

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

const fileList = ref<UploadFileInfo[]>([]);
const loading = ref(false);

async function initFileList() {
  const fileIds = props.modelValue.filter(id => id && id.trim() !== '');
  if (!props.modelValue || fileIds.length === 0) {
    fileList.value = [];
    return;
  }
  loading.value = true;
  try {
    const { data } = await safeClient(client.api.file_storage.access_urls.post({ fileIds }));

    fileList.value =
      data.value?.map(item => ({
        id: item.id,
        name: item.fileName || item.id,
        status: 'finished' as const,
        url: item.url,
        file: null as any
      })) || [];
  } catch (error) {
    window.$message?.error('加载文件列表失败');
  } finally {
    loading.value = false;
  }
}

watch(
  () => props.modelValue,
  (newVal, oldVal) => {
    if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
      initFileList();
    }
  },
  { deep: true }
);

onMounted(() => {
  initFileList();
});

async function handleCustomRequest({ file, onProgress, onFinish, onError }: UploadCustomRequestOptions) {
  try {
    const fileSizeMB = file.file!.size / 1024 / 1024;
    if (fileSizeMB > props.maxSize) {
      window.$message?.error(`文件大小不能超过 ${props.maxSize}MB`);
      onError();
      return;
    }

    if (fileList.value.length > props.maxFiles) {
      window.$message?.error(`最多只能上传 ${props.maxFiles} 个文件`);
      onError();
      return;
    }

    const options = {
      fileName: file.name,
      fileSize: file.file?.size,
      businessType: props.fetchOptions.businessType,
      ...props.fetchOptions
    } as UploadFetchOptions;

    const fileId = await uploadToS3(file.file as File, {
      fetchOptions: options,
      onProgress: percent => {
        onProgress({ percent });
      }
    });
    // 直接修改 file 对象的 id，NaiveUI 会自动同步到 fileList
    file.id = fileId;
    file.status = 'finished';
    file.url = fileId;

    const newFileIds = [...props.modelValue, fileId].filter(Boolean);
    emit('update:modelValue', newFileIds);

    onFinish();
    window.$message?.success(`文件 ${file.name} 上传成功`);
  } catch (error: any) {
    onError();
  }
}

function handleRemove({ file }: { file: UploadFileInfo }) {
  // 只删除上传成功的文件（有有效 id 的文件）
  if (file.id && typeof file.id === 'string' && props.modelValue.includes(file.id)) {
    const newFileIds = props.modelValue.filter(id => id !== file.id);
    emit('update:modelValue', newFileIds);
  }
  return true;
}

function beforeUpload({ file }: { file: UploadFileInfo }) {
  const fileSizeMB = file.file!.size / 1024 / 1024;
  if (fileSizeMB > props.maxSize) {
    window.$message?.error(`文件大小不能超过 ${props.maxSize}MB`);
    return false;
  }
  return true;
}
</script>

<template>
  <div class="upload-s3">
    <div v-if="label" class="mb-2 text-14px font-medium">{{ label }}</div>
    <NSpin :show="loading">
      <NUpload
        v-model:file-list="fileList"
        :accept="accept"
        :multiple="multiple"
        :max="maxFiles"
        :custom-request="handleCustomRequest"
        :on-remove="handleRemove"
        :on-before-upload="beforeUpload"
        list-type="text"
        show-download-button
      >
        <NButton>
          <template #icon>
            <icon-ic-round-upload class="text-icon" />
          </template>
          {{ placeholder || '选择文件' }}
        </NButton>
      </NUpload>
    </NSpin>
    <div class="mt-2 text-12px text-gray-400">
      <div v-if="maxSize">单个文件大小不超过 {{ maxSize }}MB</div>
      <div v-if="maxFiles">最多上传 {{ maxFiles }} 个文件</div>
      <div v-if="accept !== '*/*'">支持格式：{{ accept }}</div>
    </div>
  </div>
</template>

<style lang="css" scoped>
.upload-s3 {
  width: 100%;
}
</style>
