<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from 'vue';

import { Modal, Upload } from 'ant-design-vue';
import type { UploadFile } from 'ant-design-vue';

import GalleryPickLink from '#/components/form/components/gallery-pick-link.vue';
import { uploadFile } from '#/api/core/upload';
import { uploadToOss } from '#/utils/oss-upload';
import {
  beforeImageUpload,
  resolveUploadFile,
} from '#/utils/use-image-upload-pending';
import { preferences } from '@vben/preferences';
import { Icon } from '#/components/icon';

defineOptions({
  name: 'UploadImageSortable',
  inheritAttrs: false,
});

// 定义props接口
interface Props {
  modelValue: string[];
  multiple?: boolean;
  maxCount?: number;
  /**
   * 素材库可选类型（透传 GalleryPickLink）
   * xk_file.type：0=图片 1=视频；默认仅图片。不传会展示全部类型
   */
  acceptTypes?: number[];
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [],
  multiple: true,
  maxCount: 9,
  acceptTypes: () => [0],
});

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

// 定义文件项接口
interface FileItem {
  uid: string;
  name: string;
  status: 'uploading' | 'done' | 'error';
  url: string;
}

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

// 初始化fileList
const initFileList = () => {
  fileList.value = props.modelValue.map((url, index) => ({
    uid: `img-${index}-${url}`,
    name: url.split('/').pop() || 'file',
    status: 'done' as const,
    url,
  }));
};

// 初始化
initFileList();

const uploadListRef = ref<HTMLElement>();
let sortableInstance: any = null;

// 监听外部modelValue变化，同步到fileList（与upload-image.vue保持一致）
watch(() => props.modelValue, (newVal) => {
  const currentUrls = fileList.value.filter(file => file.status === 'done').map(file => file.url);
  if (JSON.stringify(newVal) !== JSON.stringify(currentUrls)) {
    fileList.value = newVal.map((url, index) => ({
      uid: `img-${index}-${url}`,
      name: url.split('/').pop() || 'file',
      status: 'done' as const,
      url,
    }));
  }
  nextTick(() => {
    initSortable();
  });
}, { deep: true, immediate: true });

// 统一通过updateModelValue函数更新modelValue（与upload-image.vue保持一致）
const updateModelValue = () => {
  const urls = fileList.value
    .filter((file) => file.status === 'done' && file.url)
    .map((file) => file.url);
  emit('update:modelValue', urls);
};

// 初始化拖拽排序（使用原生HTML5拖拽API）
const initSortable = () => {
  if (!uploadListRef.value) return;

  // 销毁旧的排序实例
  if (sortableInstance) {
    sortableInstance = null;
  }

  // 查找上传列表容器
  const uploadList = uploadListRef.value.querySelector(
    '.ant-upload-list-picture-card',
  ) as HTMLElement;
  if (!uploadList) return;

  const items = uploadList.querySelectorAll(
    '.ant-upload-list-item',
  ) as NodeListOf<HTMLElement>;

  items.forEach((item, index) => {
    item.draggable = true;
    item.style.cursor = 'move';

    item.addEventListener('dragstart', (e) => {
      e.dataTransfer!.effectAllowed = 'move';
      e.dataTransfer!.setData('text/html', index.toString());
      item.style.opacity = '0.5';
    });

    item.addEventListener('dragend', () => {
      item.style.opacity = '1';
    });

    item.addEventListener('dragover', (e) => {
      e.preventDefault();
      e.dataTransfer!.dropEffect = 'move';
    });

    item.addEventListener('drop', (e) => {
      e.preventDefault();
      const dragIndex = Number.parseInt(e.dataTransfer!.getData('text/html'));
      const dropIndex = index;

      if (dragIndex !== dropIndex) {
        const movedItem = fileList.value.splice(dragIndex, 1)[0];
        fileList.value.splice(dropIndex, 0, movedItem);
        // 拖拽排序后更新modelValue
        updateModelValue();
      }
    });
  });
};

/**
 * 自定义上传请求处理函数
 * 
 * 该函数根据preferences配置的上传方式，选择使用OSS直传或后端上传：
 * - 'direct': OSS直传模式，文件直接从浏览器上传到阿里云OSS
 * - 'backend': 后端上传模式，文件先上传到后端服务器，再由后端上传到OSS
 * 
 * 两种上传方式的区别：
 * 1. OSS直传（direct）：
 *    - 优点：减少服务器负载，上传速度更快，用户体验更好
 *    - 缺点：需要后端提供签名接口，配置相对复杂
 *    - 实现：调用uploadToOss函数，直接上传到OSS
 * 
 * 2. 后端上传（backend）：
 *    - 优点：兼容现有功能，所有上传逻辑由后端统一处理
 *    - 缺点：增加服务器负载，上传速度相对较慢
 *    - 实现：调用uploadFile API，通过后端服务器上传
 * 
 * 兼容性处理：
 * - 两种上传方式返回的数据结构保持一致：{ url: string }
 * - 确保无论使用哪种方式，组件的行为都是一致的
 * - 上传成功后，会重新初始化拖拽排序功能
 * - 如果上传失败，会更新文件状态为error，并调用onError回调
 * 
 * @param options 上传选项，包含file、onProgress、onSuccess、onError等
 */
const customRequest = async (options: any) => {
  const { file, onProgress, onSuccess, onError } = options;
  const actualFile = resolveUploadFile(file as File);

  // 创建上传中的文件项，用于在UI中显示上传状态
  const uploadingFile: FileItem = {
    uid: file.uid,
    name: file.name,
    status: 'uploading',
    url: '',
  };

  // 将上传中的文件添加到文件列表
  fileList.value = [...fileList.value, uploadingFile];

  try {
    // 从preferences中读取上传方式配置
    // uploadMethod可能的值：'direct'（OSS直传）或 'backend'（后端上传）
    const uploadMethod = preferences.app.uploadMethod || 'direct';

    let res: { url: string };

    // 根据配置选择上传方式
    if (uploadMethod === 'direct') {
      // OSS直传模式：文件直接从浏览器上传到OSS
      // uploadToOss函数会：
      // 1. 从后端获取OSS签名信息
      // 2. 生成全局唯一的文件名（UUID + 时间戳）
      // 3. 使用FormData构造POST请求直接上传到OSS
      // 4. 处理上传进度和错误
      // 5. 返回上传后的文件URL
      res = await uploadToOss({
        file: actualFile,
        onProgress: (percent) => {
          // 将OSS上传进度传递给组件
          // percent范围：0-100，表示上传百分比
          if (onProgress) {
            onProgress(percent);
          }
        },
      });
    } else {
      // 后端上传模式：文件先上传到后端服务器，再由后端上传到OSS
      // uploadFile函数会：
      // 1. 将文件发送到后端API（/upload/image）
      // 2. 后端接收文件后上传到OSS
      // 3. 返回上传后的文件URL
      res = await uploadFile({
        file: actualFile,
      });
    }

    // 上传成功，更新文件状态为完成
    // 将上传结果中的URL赋值给文件项
    const updatedFileList = fileList.value.map(item =>
      item.uid === file.uid
        ? { ...item, status: 'done' as const, url: res.url }
        : item
    );

    fileList.value = updatedFileList;
    
    // 更新组件的modelValue，触发父组件的更新
    updateModelValue();

    // 重新初始化拖拽排序功能
    // 因为文件列表发生了变化，需要重新绑定拖拽事件
    nextTick(() => {
      initSortable();
    });

    // 调用成功回调，通知上传组件上传已完成
    onSuccess(res);
  } catch (error) {
    // 上传失败，记录错误信息
    console.error('上传失败', error);
    
    // 更新文件状态为错误
    // 错误状态的文件会在UI中显示错误图标
    const updatedFileList = fileList.value.map(item =>
      item.uid === file.uid
        ? { ...item, status: 'error' as const }
        : item
    );

    fileList.value = updatedFileList;
    
    // 调用错误回调，通知上传组件上传失败
    onError(error);
  }
};

// 处理文件删除（与upload-image.vue保持一致）
const handleRemove = (file: UploadFile) => {
  fileList.value = fileList.value.filter((item) => item.uid !== file.uid);
  updateModelValue();

  // 重新初始化排序
  nextTick(() => {
    initSortable();
  });
};

// 预览功能（与upload-image.vue保持一致）
const previewVisible = ref(false);
const previewImage = ref('');
const previewTitle = ref('');

const handlePreview = async (file: UploadFile) => {
  previewImage.value = file.url || '';
  previewVisible.value = true;
  previewTitle.value = file.name || file.url?.split('/').pop() || '';
};

function onGallerySelect(urls: string[]) {
  const remain = props.maxCount - fileList.value.length;
  const picked = props.multiple ? urls.slice(0, remain) : urls.slice(0, 1);
  for (const url of picked) {
    if (fileList.value.some((f) => f.url === url)) {
      continue;
    }
    fileList.value = [
      ...fileList.value,
      {
        uid: `gallery-${url}`,
        name: url.split('/').pop() || 'file',
        status: 'done' as const,
        url,
      },
    ];
  }
  updateModelValue();
  nextTick(() => {
    initSortable();
  });
}

// 计算是否显示上传按钮（与upload-image.vue保持一致）
const showUploadButton = computed(() =>
  props.multiple
    ? fileList.value.length < props.maxCount
    : fileList.value.length === 0,
);

const galleryRemainCount = computed(() => Math.max(props.maxCount - fileList.value.length, 0));

onMounted(() => {
  nextTick(() => {
    initSortable();
  });
});
</script>

<template>
  <div ref="uploadListRef" class="upload-image-wrap">
    <Upload
      :file-list="fileList"
      :before-upload="beforeImageUpload"
      :custom-request="customRequest"
      :multiple="multiple"
      :max-count="maxCount"
      :show-upload-list="{
        showPreviewIcon: true,
        showRemoveIcon: true,
      }"
      list-type="picture-card"
      @remove="handleRemove"
      @preview="handlePreview"
      accept="image/*"
    >
      <div v-if="showUploadButton" class="upload-button">
        <Icon icon="ant-design:plus-outlined" />
        <div class="ant-upload-text">上传图片</div>
      </div>
    </Upload>

    <GalleryPickLink
      v-if="showUploadButton"
      :multiple="multiple"
      :max-count="galleryRemainCount"
      :accept-types="acceptTypes"
      @select="onGallerySelect"
    />

    <Modal
      v-model:visible="previewVisible"
      :title="previewTitle"
      footer=""
      width="60%"
    >
      <img alt="预览图片" style="width: 100%" :src="previewImage" />
    </Modal>
  </div>
</template>

<style lang="less" scoped>
.upload-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  cursor: pointer;
}

:deep(.ant-upload-list-picture-card .ant-upload-list-item) {
  cursor: move;
}
</style>
