<template>
  <div class="iqudoo-form-file-upload">
    <div>
      <!-- 文件列表 -->
      <div class="file-list-wrapper" v-if="fileFormModel.length > 0">
        <a-list size="small" :data-source="fileFormModel" class="file-list">
          <template #renderItem="{ item, index }">
            <a-list-item>
              <div
                class="file-item"
                :class="{
                  dragging: draggingIndex === index,
                  'drag-over': draggedOverIndex === index && draggingIndex !== index,
                }"
                :draggable="dragToSort && !field.disabled && !readOnly"
                @dragstart="handleDragStart(index)"
                @dragend="handleDragEnd"
                @dragover="handleDragOver"
                @dragenter="handleDragEnter(index)"
                @dragleave="handleDragLeave"
                @drop="handleDrop(index)"
              >
                <div class="file-icon">
                  <file-text-outlined v-if="isFileType(item, 'txt')" />
                  <file-pdf-outlined v-else-if="isFileType(item, 'pdf')" />
                  <file-excel-outlined v-else-if="isFileType(item, 'excel')" />
                  <file-word-outlined v-else-if="isFileType(item, 'word')" />
                  <file-zip-outlined v-else-if="isFileType(item, 'zip')" />
                  <file-outlined v-else />
                </div>
                <div class="file-content">
                  <div class="file-name-row">
                    <a class="file-name" :href="item" target="_blank">
                      {{ getFileName(item) }}
                    </a>
                    <div class="file-actions" v-if="!field.disabled && !readOnly">
                      <a-button type="link" @click="downloadFile(item)">
                        <template #icon><download-outlined /></template>
                      </a-button>
                      <a-button type="link" danger @click="removeFile(index)">
                        <template #icon><delete-outlined /></template>
                      </a-button>
                    </div>
                  </div>
                  <div class="file-info">{{ getFileTypeLabel(item) }}</div>
                </div>
                <div
                  class="drag-handle"
                  v-if="dragToSort && !field.disabled && !readOnly"
                >
                  <holder-outlined />
                </div>
              </div>
            </a-list-item>
          </template>
        </a-list>
      </div>

      <!-- 上传触发区域 -->
      <div
        v-show="readOnly || fileFormModel.length < field.fileCount"
        class="upload-trigger"
      >
        <a-upload
          :id="'form_item_' + formatFieldName(field.name)"
          :max-count="field.fileCount"
          :show-upload-list="false"
          :before-upload="beforeUpload"
          :accept="acceptedImageTypes"
          :multiple="multipleSelect"
          list-type="picture-card"
        >
          <div class="upload-button">
            <a-spin :spinning="loading">
              <plus-outlined />
              <div class="upload-text">
                {{ multipleSelect ? "选择多个文件" : "上传文件" }}
              </div>
            </a-spin>
          </div>
        </a-upload>
      </div>
    </div>
    <div class="upload-hint">
      <span>
        {{ fileFormModel.length }}/{{ field.fileCount }}个
        <template v-if="field.fileSize"> · 最大{{ field.fileSize }}MB </template>
        <template v-if="acceptDescription"> · 支持{{ acceptDescription }} </template>
        <template v-if="multipleSelect"> · 支持多选和拖动排序 </template>
      </span>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from "vue";
import { useVModel } from "@/iview/utils/useModel";
import { apiRequest } from "@/iview/utils/request";
import { showErrorTips } from "@/iview/utils/messageTips";
import {
  DeleteOutlined,
  DownloadOutlined,
  PlusOutlined,
  FileTextOutlined,
  FilePdfOutlined,
  FileExcelOutlined,
  FileWordOutlined,
  FileZipOutlined,
  FileOutlined,
  HolderOutlined,
} from "@ant-design/icons-vue";

const props = defineProps({
  field: {
    type: Object,
    required: true,
  },
  accept: {
    type: String,
    default: ".pdf,.doc,.docx,.xls,.xlsx,.zip,.rar,.txt",
  },
  multipleSelect: {
    type: Boolean,
    default: false,
  },
  dragToSort: {
    type: Boolean,
    default: true,
  },
  fieldPrefix: {
    type: String,
    default: "",
  },
  modelValue: {
    type: Object,
    required: true,
  },
  readOnly: {
    type: Boolean,
    default: false,
  },
  rawFormData: {
    type: Object,
  },
});

const emit = defineEmits(["update:modelValue"]);
const vModel = useVModel(props, "modelValue", emit);

const loading = ref(false);
const draggingIndex = ref(-1);
const draggedOverIndex = ref(-1);
const dragPlaceholder = ref(null);

const acceptDescription = computed(() => {
  const types = props.accept
    .split(",")
    .map((type) => {
      return type.replace(".", "").toUpperCase();
    })
    .join("、");
  return types;
});

const acceptedImageTypes = computed(() => {
  return (
    props.accept ||
    "application/pdf,application/excel,application/word,application/zip,text/plain"
  );
});

const formatFieldName = (name) => {
  if (!props.fieldPrefix) {
    return name;
  }
  return props.fieldPrefix + "-" + name;
};

// 判断文件类型
const isFileType = (url, type) => {
  const fileName = getFileName(url).toLowerCase();
  if (type === "pdf") {
    return fileName.endsWith(".pdf");
  } else if (type === "excel") {
    return fileName.endsWith(".xls") || fileName.endsWith(".xlsx");
  } else if (type === "word") {
    return fileName.endsWith(".doc") || fileName.endsWith(".docx");
  } else if (type === "zip") {
    return fileName.endsWith(".zip") || fileName.endsWith(".rar");
  } else if (type === "txt") {
    return fileName.endsWith(".txt") || fileName.endsWith(".log");
  }
  return true;
};

// 获取文件类型标签
const getFileTypeLabel = (url) => {
  const fileName = getFileName(url).toLowerCase();
  let fileType = "文件";

  if (fileName.endsWith(".pdf")) {
    fileType = "PDF文档";
  } else if (fileName.endsWith(".doc") || fileName.endsWith(".docx")) {
    fileType = "Word文档";
  } else if (fileName.endsWith(".xls") || fileName.endsWith(".xlsx")) {
    fileType = "Excel表格";
  } else if (fileName.endsWith(".zip") || fileName.endsWith(".rar")) {
    fileType = "压缩包";
  } else if (fileName.endsWith(".txt")) {
    fileType = "文本文件";
  }

  return fileType;
};

const getFileName = (url) => {
  if (!url) return "未知文件";
  const parts = url.split("/");
  return parts[parts.length - 1];
};

const removeFile = (index) => {
  let newFileList = [...fileFormModel.value];
  newFileList.splice(index, 1);
  vModel.value[formatFieldName(props.field.name)] = newFileList.join("|");
};

const downloadFile = (url) => {
  if (!url) return;
  window.open(url, "_blank");
};

const beforeUpload = async (file, fileList) => {
  // 如果是多选模式且是第一个文件，处理所有文件
  if (
    props.multipleSelect &&
    fileList &&
    fileList.length > 1 &&
    fileList.indexOf(file) === 0
  ) {
    await uploadMultipleFiles(fileList);
    return false;
  }

  // 如果是多选模式但不是第一个文件，跳过处理
  if (
    props.multipleSelect &&
    fileList &&
    fileList.length > 1 &&
    fileList.indexOf(file) > 0
  ) {
    return false;
  }

  // 单个文件处理
  if (props.accept) {
    const acceptTypes = props.accept.split(",");
    const fileName = file.name.toLowerCase();
    const isAccepted = acceptTypes.some((type) => {
      return fileName.endsWith(type.replace(".", ""));
    });

    if (!isAccepted) {
      let error = new Error(`只能上传${acceptDescription.value}格式的文件!`);
      showErrorTips(error);
      return false;
    }
  }

  if (
    props.field.fileCount &&
    props.field.fileCount > 0 &&
    fileFormModel.value.length >= props.field.fileCount
  ) {
    let error = new Error(`最多只能上传${props.field.fileCount}个文件`);
    showErrorTips(error);
    return false;
  }

  if (
    props.field.fileSize &&
    props.field.fileSize > 0 &&
    file.size > props.field.fileSize * 1024 * 1024
  ) {
    let error = new Error(`文件大小不能超过${props.field.fileSize}MB`);
    showErrorTips(error);
    return false;
  }

  await uploadFile(file);
  return false;
};

const uploadFile = async (file) => {
  try {
    loading.value = true;

    const base64 = await fileToBase64(file);

    const result = await apiRequest("api.system.global.file.upload", {
      fileBase64: base64.split(",")[1],
      type: "file",
      fileName: file.name,
    });

    if (!result || !result.url) {
      throw new Error("文件上传失败，未获取到URL");
    }
    let newFileList = [...fileFormModel.value];
    newFileList.push(result.url);
    vModel.value[formatFieldName(props.field.name)] = newFileList.join("|");
  } catch (error) {
    showErrorTips(error);
  } finally {
    loading.value = false;
  }
};

const fileToBase64 = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const base64String = reader.result;
      resolve(base64String);
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
};

const fileFormModel = computed(() => {
  return vModel.value[formatFieldName(props.field.name)]
    .split("|")
    .filter((item) => !!item);
});

// 多文件上传
const uploadMultipleFiles = async (fileList) => {
  try {
    loading.value = true;

    // 检查文件数量限制
    const currentFileCount = fileFormModel.value.length;
    const maxFileCount = props.field.fileCount || 0;
    const remainingSlots = maxFileCount - currentFileCount;

    if (remainingSlots <= 0) {
      let error = new Error(`最多只能上传${maxFileCount}个文件`);
      showErrorTips(error);
      return;
    }

    // 验证每个文件的大小和类型
    const validFiles = [];
    for (const file of fileList) {
      // 检查文件类型
      if (props.accept) {
        const acceptTypes = props.accept.split(",");
        const fileName = file.name.toLowerCase();
        const isAccepted = acceptTypes.some((type) => {
          return fileName.endsWith(type.replace(".", ""));
        });

        if (!isAccepted) {
          showErrorTips(
            new Error(
              `文件 ${file.name} 格式不支持，只支持${acceptDescription.value}格式`
            )
          );
          continue;
        }
      }

      // 检查文件大小
      if (
        props.field.fileSize &&
        props.field.fileSize > 0 &&
        file.size > props.field.fileSize * 1024 * 1024
      ) {
        showErrorTips(
          new Error(`文件 ${file.name} 大小超过限制，最大${props.field.fileSize}MB`)
        );
        continue;
      }

      validFiles.push(file);

      // 如果已达到剩余槽位限制，停止添加
      if (validFiles.length >= remainingSlots) {
        break;
      }
    }

    if (validFiles.length === 0) {
      showErrorTips(new Error("没有有效的文件可以上传"));
      return;
    }

    if (validFiles.length < fileList.length) {
      showErrorTips(
        new Error(
          `选择了${fileList.length}个文件，但只有${validFiles.length}个文件符合要求，已自动过滤无效文件`
        )
      );
    }

    if (validFiles.length > remainingSlots) {
      showErrorTips(
        new Error(
          `最多还能上传${remainingSlots}个文件，已自动选择前${remainingSlots}个有效文件`
        )
      );
    }

    // 限制本次上传的文件数量
    const filesToUpload = validFiles.slice(0, remainingSlots);

    const uploadPromises = filesToUpload.map((file) => uploadSingleFile(file));
    const results = await Promise.all(uploadPromises);

    // 过滤掉失败的上传
    const successfulUploads = results.filter((result) => result && result.url);

    if (successfulUploads.length > 0) {
      let newFileList = [...fileFormModel.value];
      newFileList.push(...successfulUploads.map((result) => result.url));
      vModel.value[formatFieldName(props.field.name)] = newFileList.join("|");
    }
  } catch (error) {
    showErrorTips(error);
  } finally {
    loading.value = false;
  }
};

// 单个文件上传（不更新UI状态）
const uploadSingleFile = async (file) => {
  try {
    const base64 = await fileToBase64(file);
    const result = await apiRequest("api.system.global.file.upload", {
      fileBase64: base64.split(",")[1],
      type: "file",
      fileName: file.name,
    });
    return result;
  } catch (error) {
    console.error("文件上传失败:", error);
    return null;
  }
};

// 拖动排序功能
const handleDragStart = (index) => {
  if (!props.dragToSort || props.field.disabled || props.readOnly) return;

  draggingIndex.value = index;
  draggedOverIndex.value = -1;
  // 设置拖拽数据
  event.dataTransfer.effectAllowed = "move";
  event.dataTransfer.setData("text/html", index.toString());
};

const handleDragEnd = () => {
  draggingIndex.value = -1;
  draggedOverIndex.value = -1;
};

const handleDragOver = (event) => {
  if (!props.dragToSort || props.field.disabled || props.readOnly) return;

  event.preventDefault();
  event.dataTransfer.dropEffect = "move";
};

const handleDragEnter = (index) => {
  if (!props.dragToSort || props.field.disabled || props.readOnly) return;

  if (draggingIndex.value !== -1 && draggingIndex.value !== index) {
    draggedOverIndex.value = index;
  }
};

const handleDragLeave = (event) => {
  if (!props.dragToSort || props.field.disabled || props.readOnly) return;

  // 只有当离开整个拖拽区域时才清除dragOver状态
  const relatedTarget = event.relatedTarget;
  if (!relatedTarget || !event.currentTarget.contains(relatedTarget)) {
    draggedOverIndex.value = -1;
  }
};

const handleDrop = (dropIndex) => {
  if (!props.dragToSort || props.field.disabled || props.readOnly) return;

  try {
    event.preventDefault();
  } catch (_e) {}

  const dragIndex = draggingIndex.value;
  if (dragIndex === -1 || dragIndex === dropIndex) {
    draggingIndex.value = -1;
    draggedOverIndex.value = -1;
    return;
  }

  const newFileList = [...fileFormModel.value];
  const draggedItem = newFileList[dragIndex];

  // 移除被拖动的项目
  newFileList.splice(dragIndex, 1);

  // 计算正确的插入位置
  let insertIndex = dropIndex;

  // 在目标位置插入
  newFileList.splice(insertIndex, 0, draggedItem);

  // 更新数据
  vModel.value[formatFieldName(props.field.name)] = newFileList.join("|");

  draggingIndex.value = -1;
  draggedOverIndex.value = -1;
};
</script>

<style lang="less" scoped>
.iqudoo-form-file-upload {
  width: 100%;

  .ant-list-item {
    padding: 8px;
  }

  .file-list-wrapper {
    margin-bottom: 16px;
  }

  .file-list {
    border: 1px solid #f0f0f0;
    border-radius: 8px;
    overflow: hidden;
    background-color: #fafafa;
  }

  .file-item {
    display: flex;
    width: 100%;
    align-items: flex-start;
    padding: 8px;
    cursor: move;
    transition: all 0.2s ease;
    border-radius: 8px;
    position: relative;
    border: 1px solid transparent;
  }

  .file-item:hover {
    background-color: #f5f5f5;
  }

  .file-item.dragging {
    opacity: 0.6;
    transform: rotate(1deg) scale(1.01);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    z-index: 1000;
    background-color: #fff;
    border-color: #1890ff;
  }

  .file-item.drag-over {
    border: 2px dashed #1890ff;
    background-color: rgba(24, 144, 255, 0.05);
    transform: scale(1.01);
  }

  .file-icon {
    font-size: 24px;
    margin-right: 12px;
    color: #1890ff;
  }

  .file-content {
    flex: 1;
    min-width: 0;
  }

  .file-name-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
  }

  .file-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: #1890ff;
    font-weight: 500;
  }

  .file-actions {
    display: flex;
    white-space: nowrap;
  }

  .drag-handle {
    margin-left: 8px;
    color: #999;
    cursor: move;
    display: flex;
    align-items: center;
    padding: 4px;
    border-radius: 4px;
    transition: all 0.2s;
  }

  .drag-handle:hover {
    color: #1890ff;
    background-color: #f0f0f0;
  }

  .file-info {
    font-size: 12px;
    color: #999;
    margin-top: 2px;
  }

  .upload-trigger {
    margin-bottom: 16px;
  }

  .upload-button {
    height: 38px;
    border-radius: 6px;
    width: 100%;
    max-width: 200px;
  }

  .upload-hint {
    font-size: 12px;
    color: #999;
    display: flex;
    flex-wrap: wrap;
  }

  @media (max-width: 768px) {
    .file-name-row {
      flex-direction: column;
      align-items: flex-start;
    }

    .file-actions {
      margin-top: 4px;
    }

    .upload-hint {
      flex-direction: column;
      gap: 4px;
    }
  }
}
</style>
