<script setup lang="ts">
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';

import { defineExpose, defineProps, nextTick, ref, watch } from 'vue';
import { Attachments } from 'vue-element-plus-x';

import { ElMessage } from 'element-plus';

import { api } from '#/api/request';

const props = defineProps({
  maxSize: {
    type: Number,
    default: 2,
  },
  action: {
    type: String,
    default: '/api/v1/commons/upload',
  },
  externalFiles: {
    type: Array as () => File[],
    default: () => [],
  },
});

const emit = defineEmits(['deleteAll']);
type SelfFilesCardProps = {
  fileSize: number;
  id?: number;
  name: string;
  uid: number | string;
  url: string; // 上传后的文件地址
} & FilesCardProps;

const files = ref<SelfFilesCardProps[]>([]);
const fileInputRef = ref<HTMLInputElement>();
/**
 * 上传前校验
 */
function handleBeforeUpload(file: File) {
  const maxSizeBytes = props.maxSize * 1024 * 1024;
  if (file.size > maxSizeBytes) {
    ElMessage.error(`文件大小不能超过 ${props.maxSize}MB!`);
    return false;
  }
  return true;
}

/**
 * 自定义上传请求
 */
async function handleHttpRequest(options: { file: File }) {
  const { file } = options;

  try {
    const res = await api.upload(props.action, { file }, {});

    const fileUrl = res.data.path;
    const fileItem: SelfFilesCardProps = {
      id: files.value.length,
      uid: Date.now() + Math.random(),
      name: file.name,
      fileSize: file.size,
      url: fileUrl,
      showDelIcon: true,
      imgVariant: 'square',
    };

    files.value.push(fileItem);
  } catch (error) {
    ElMessage.error(`${file.name} 上传失败`);
    console.error('上传失败:', error);
  }
}

/**
 * 批量上传外部文件（父组件传递的文件数组）
 */
async function uploadExternalFiles(fileList: File[]) {
  if (fileList.length === 0) return;

  for (const file of fileList) {
    if (handleBeforeUpload(file)) {
      await handleHttpRequest({ file });
    }
  }
}

/**
 * 处理原生文件选择
 */
async function handleFileInputChange(event: Event) {
  const target = event.target as HTMLInputElement;
  const selectedFiles = Array.from(target.files || []);
  if (selectedFiles.length > 0) {
    await uploadExternalFiles(selectedFiles);
  }
  target.value = '';
}

/**
 * 组件内部完成删除逻辑
 */
function handleDeleteCard(item: FilesCardProps) {
  const targetItem = item as SelfFilesCardProps;
  files.value = files.value.filter((file) => file.id !== targetItem.id);
  if (files.value.length === 0) {
    emit('deleteAll');
  }
}
/**
 * 暴露给父组件的核心方法：仅返回URL字符串数组
 * @returns string[] 纯文件地址列表
 */
function getFileList(): string[] {
  return files.value.map((file) => file.url);
}

/**
 * 触发原生文件选择
 */
function triggerFileSelect() {
  fileInputRef.value?.click();
}

watch(
  () => props.externalFiles,
  async (newFiles) => {
    if (newFiles.length > 0) {
      await nextTick();
      await uploadExternalFiles(newFiles);
    }
  },
  { deep: true, immediate: true },
);

defineExpose({
  getFileList,
  triggerFileSelect,
  clearFiles() {
    files.value = [];
  },
});
</script>

<template>
  <div v-if="files.length > 0" style="display: flex; flex-direction: column; gap: 12px">
    <Attachments
      :items="files"
      :hide-upload="true"
      @delete-card="handleDeleteCard"
    />
  </div>
  <input
    ref="fileInputRef"
    type="file"
    multiple
    class="hidden"
    @change="handleFileInputChange"
  />
</template>

<style scoped lang="less"></style>
