<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import type { UploadFileInfo, UploadInst } from 'naive-ui';
import { isExternal } from '@/utils/ruoyi';
import { getServiceBaseURL } from '@/utils/service';
import { getAuthorization } from '@/service/request/shared';

defineOptions({
  name: 'FileUpload'
});

// 上传服务器地址
// const baseUrl = import.meta.env.VITE_BASE_URL;
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
const uploadUrl = `${import.meta.env.VITE_SERVICE_BASE_URL}/${import.meta.env.VITE_SERVICE_UPLOAD}`;
const upload = ref<UploadInst | null>(null);

const headers = {
  Authorization: getAuthorization()
};

interface Props {
  maxCount?: number; // 最大上传数量
  maxSize?: number; // 最大文件大小(MB)
  accept?: string[]; // 允许的文件类型
  multiple?: boolean; // 是否支持多选
  draggable?: boolean; // 是否支持拖拽上传
  defaultUpload?: boolean; // 是否默认上传
  url?: string; // 上传地址
}

const props = withDefaults(defineProps<Props>(), {
  maxCount: 5,
  maxSize: 15,
  accept: () => ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'txt', 'pdf', 'png', 'jpg', 'jpeg'],
  multiple: true,
  draggable: false,
  defaultUpload: true,
  url: ''
});

const modelValue = defineModel<string>('modelValue');

const isMultiple = computed(() => {
  if (props.maxCount === 1) return false;
  return props.multiple;
});

// 文件列表
const fileList = ref<UploadFileInfo[]>([]);

const action = computed(() => {
  if (props.url) {
    return `${import.meta.env.VITE_SERVICE_BASE_URL}/${props.url}`;
  }
  return uploadUrl;
});

const emit = defineEmits<{
  (e: 'success', value: { url: string; name: string }): void;
  (e: 'remove'): void;
}>();

watch(
  () => modelValue.value,
  val => {
    if (val) {
      // 将值转换为数组
      let list: string[] = [];

      // 确保 list 是数组，如果是字符串，则 split 成数组
      if (Array.isArray(val)) {
        list = val;
      } else if (typeof val === 'string') {
        list = val.split(',');
      }

      // 转换为符合要求的对象数组
      fileList.value = list.map((item, index) => {
        let name = '';
        let url = '';

        if (typeof item === 'string') {
          // 检查是否为外部链接或需要补全 baseURL
          if (!item.includes(baseURL) && !isExternal(item)) {
            url = `${baseURL}${item}`;
            name = item.split('/').pop() || '未知文件';
          } else {
            url = item;
            name = item.split('/').pop() || '未知文件';
          }
        }
        return {
          id: String(index + 1),
          name,
          status: 'finished',
          url
        };
      });
    } else {
      fileList.value = [];
    }
    modelValue.value = listToString(fileList.value);
  },
  { deep: true, immediate: true }
);

// 上传前校验
const beforeUpload = (data: { file: UploadFileInfo }) => {
  // 检查文件大小
  if (data.file.file) {
    const sizeInMB = data.file.file.size / 1024 / 1024;
    if (sizeInMB > props.maxSize) {
      window.$message?.error(`文件大小不能超过${props.maxSize}MB`);
      return false;
    }
  }
  // 检查文件类型
  if (data.file.file) {
    const fileName = data.file.file.name;
    const ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
    if (!props.accept.includes(ext)) {
      window.$message?.error(`只支持${props.accept.join('、')}格式`);
      return false;
    }
  }
  return true;
};

// 上传完成
const handleSuccess = ({ event }: { event: Event }) => {
  const target = event.target as XMLHttpRequest;
  const res = JSON.parse(target.response);
  if (res.code === 200) {
    fileList.value.push({
      id: String(fileList.value.length + 1),
      name: res.data.name,
      status: 'finished',
      url: res.data.url
    });
    modelValue.value = listToString(fileList.value);
    emit('success', {
      url: res.data.url,
      name: res.data.name
    });
    window.$message?.success('上传成功');
  } else {
    window.$message?.error(res.msg || '上传失败');
  }
};

// 删除文件
const handleRemove = ({ index }: { index: number }) => {
  fileList.value.splice(index, 1);
  modelValue.value = listToString(fileList.value);
  emit('remove');
};

// 对象转成指定字符串分隔
function listToString(list: UploadFileInfo[], separator = ',') {
  return list
    .map(item => item.url)
    .filter(url => url && !url.startsWith('blob:'))
    .join(separator);
}

function handleSubmit() {
  upload.value?.submit();
}
</script>

<template>
  <div>
    <NUpload
      ref="upload"
      :action="action"
      :headers="headers"
      :max="maxCount"
      :default-file-list="fileList"
      list-type="image"
      :accept="accept.map(item => `.${item}`).join(',')"
      :multiple="isMultiple"
      :default-upload="defaultUpload"
      @before-upload="beforeUpload"
      @finish="handleSuccess"
      @remove="handleRemove"
    >
      <NUploadDragger v-if="draggable">
        <div class="flex items-center justify-center">
          <div class="text-center">
            <div class="mb-16px text-24px">📤</div>
            <div class="mb-8px text-14px">
              拖拽文件至此，或者
              <span class="cursor-pointer text-primary">选择文件</span>
            </div>
            <div class="label-color text-12px">已支持{{ accept.join('、') }}文件，不超过 {{ maxSize }}MB。</div>
          </div>
        </div>
      </NUploadDragger>
      <NButtonGroup>
        <NUploadTrigger v-if="!draggable" #="{ handleClick }" abstract>
          <NButton type="primary">{{ !defaultUpload ? '上传文件' : '选择文件' }}</NButton>
        </NUploadTrigger>
        <NButton v-if="!defaultUpload" type="success" @click.stop="handleSubmit" :class="{ 'mt-15px': draggable }">
          上传服务器
        </NButton>
      </NButtonGroup>
    </NUpload>
    <template v-if="!draggable">
      <NP>
        请上传不超过
        <span class="mx-1px text-error">{{ maxSize }}</span>
        MB，格式为
        <span class="mx-1px text-error">{{ accept.join('、') }}</span>
        的文件！
      </NP>
    </template>
  </div>
</template>

<style scoped></style>
