<script setup lang="ts">
import type { MessageReactive } from 'naive-ui'
import { useMessage } from 'naive-ui'
import SparkMD5 from 'spark-md5'
import { onMounted, ref } from 'vue'

// const { calculateHash } = useFileHash();
// const { calculateHash, isCalculating } = useFileHash()
// debugger
// Props 定义
const props = defineProps({
  accept: {
    type: Array as PropType<string[]>,
    default: () => ['doc', 'docx', 'pdf', 'safetensors', 'ckpt'],
  },
  required: {
    type: Boolean,
    default: false,
  },
  fileSize: {
    type: Number,
    default: 2000,
  },
  verifyHash: {
    type: Boolean,
    default: false,
  },
  verifyName: {
    type: Boolean,
    default: false,
  },
  type: {
    type: String,
    default: 'model',
  },
})
// 定义上传完成后发送的事件
const emit = defineEmits<{
  'upload-success': [
    {
      objectKey: string
      objectUrl: string
      fileName: string
      currentFileSize: number
      hashCode: number
    },
  ]
}>()
const messageReactive: MessageReactive | null = null
// 响应式状态
const fileInputRef = ref<HTMLInputElement | null>(null)
const file = ref<File | null>(null)
const fileName = ref('')
const fileSize = ref(0)
const progress = ref(0)
const objectKey = ref('')
const chunkSize = ref(5 * 1024 * 1024)
const totalChunks = ref(0)
const currentChunk = ref(1)
const uploadId = ref('')
const partArr = ref([])
const showFileName = ref(false)
const bucketName = ref('')
const objectKeyName = ref('')
const requiredValid = ref(false)
const hashCode = ref('')
const message = useMessage()
const currentFileSize = ref(0)

// 添加新的状态来跟踪上传状态
const uploadStatus = ref<'idle' | 'uploading' | 'paused' | 'completed'>('idle')
const uploadedChunks = ref<Set<number>>(new Set())
const retryCount = ref<number>(0)
const maxRetries = 5 // 最大重试次数

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

// 获取接受的文件类型
function getAcceptType() {
  return props.accept.join(',')
}

// 添加计算hash的函数
function calculateFileHash(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsArrayBuffer(file)

    reader.onload = (event) => {
      try {
        const buffer = event.target?.result as ArrayBuffer
        const spark = new SparkMD5.ArrayBuffer()
        spark.append(buffer)
        const hash = spark.end()
        resolve(hash)
      }
      catch (error) {
        reject(error)
      }
    }

    reader.onerror = () => {
      reject(new Error('Failed to read file'))
    }
  })
}
// const calculateFileHash = (file: File): Promise<string> => {
//   return new Promise((resolve, reject) => {
//     const reader = new FileReader();
//     reader.readAsArrayBuffer(file);

//     reader.onload = async (event) => {
//       try {
//         const buffer = event.target?.result as ArrayBuffer;
//         const hashBuffer = await window.crypto.subtle.digest("SHA-256", buffer);
//         const hashArray = Array.from(new Uint8Array(hashBuffer));
//         const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
//         resolve(hashHex);
//       } catch (error) {
//         reject(error);
//       }
//     };

//     reader.onerror = () => {
//       reject(new Error("Failed to read file"));
//     };
//   });
// };
// 生成唯一ID
// const guid = () => {
//   return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
//     const r = (Math.random() * 16) | 0;
//     const v = c === "x" ? r : (r & 0x3) | 0x8;
//     return v.toString(16);
//   });
// };

// 初始化参数
function initParam() {
  file.value = null
  objectKey.value = ''
  totalChunks.value = 0
  currentChunk.value = 1
  uploadId.value = ''
  partArr.value = []
  requiredValid.value = false
  uploadStatus.value = 'idle'
  uploadedChunks.value.clear()
  retryCount.value = 0
}

function getOssDefaultPath(name: any) {
  const timestamp = Date.now()
  const now = new Date()
  const year = now.getFullYear() // 获取当前年份
  const month = String(now.getMonth() + 1).padStart(2, '0') // 获取当前月份，确保两位
  const day = String(now.getDate()).padStart(2, '0') // 获取当前日期，确保两位

  return `${year}/${month}/${day}/${timestamp}/${name}` // 拼接路径
}
// 选择文件处理
async function selectFile(event: Event) {
  const target = event.target as HTMLInputElement
  if (!target.files?.length)
    return

  file.value = target.files[0]
  target.value = ''

  if (file.value?.size) {
    currentFileSize.value = file.value?.size
    // 文件大小检查
    const calculatedFileSize = Math.round((file.value.size / 1024 / 1024) * 100) / 100
    if (calculatedFileSize > props.fileSize) {
      message.warning(`文件大小超过上限${props.fileSize}MB`)
      return
    }
    fileSize.value = calculatedFileSize

    // 文件类型检查
    const selectedFileName = file.value.name
    const fileType = selectedFileName.split('.').pop()?.toLowerCase()
    if (!fileType || !props.accept.includes(fileType)) {
      message.warning('文件类型不支持')
      return
    }

    try {
      // 如果存在未完成的上传，询问是否继续
      if (uploadStatus.value === 'paused' && file.value) {
        const shouldResume = window.confirm('检测到未完成的上传，是否继续上传？')
        if (shouldResume) {
          await resumeUpload()
          return
        }
        else {
          // 用户选择重新上传，清除之前的上传状态
          initParam()
        }
      }

      // 继续上传流程
      progress.value = 0
      fileName.value = file.value.name
      showFileName.value = true

      // 计算文件hash
      if (props.verifyHash) {
        message.info('正在校验文件hash...')
        try {
          hashCode.value = await calculateFileHash(file.value)
        }
        catch (err) {
          message.warning(err)
          hashCode.value = ''
        }
        finally {
          // messageReactive.destroy();
          // messageReactive = null;
        }
        // hashCode.value = await calculateHash(file.value)
        // hashCode.value = await calculateFileHash(file.value);
        // 检查文件是否已存在
        try {
          const hashRes = await request.get(
            `/file/selectHash?hashCode=${hashCode.value}&type=${
              props.type === 'model' ? 0 : 1
            }`,
          )
          if (hashRes.data !== 1) {
            message.warning('文件已存在')
            return
          }
        }
        finally {
          if (messageReactive) {
            // messageReactive.destroy();
            // messageReactive = null;
          }
        }
      }

      //   校验名称
      if (props.verifyName) {
        message.info('正在校验文件名...')
        // 检查文件是否已存在
        // 上传文件前先校验是否存在  存在为0 不存在为1
        try {
          // const res = await request.get(
          //   `/file/selectFile?type=${props.type}&name=${fileName.value}`
          // );
          const res = await request.post(
            `/file/selectFile`,
            {
              type: props.type,
              name: fileName.value,
            },
          )
          if (res.data !== 1) {
            message.warning('文件名已存在')
            return
          }
        }
        finally {
          if (messageReactive) {
            // messageReactive.destroy();
            // messageReactive = null;
          }
        }
      }

      totalChunks.value = Math.ceil(file.value.size / chunkSize.value)
      objectKey.value = `${getOssDefaultPath(fileName.value)}`
      // 获取上传ID
      const res = await request.get(`/file/getUploadId?objectKey=${objectKey.value}`)
      uploadId.value = res.data
      progress.value = 1
      uploadFile()
    }
    catch (error) {
      console.error('文件处理失败:', error)
      message.error('文件处理失败')
      initParam()
    }
  }
  //   const target = event.target as HTMLInputElement;
  //   if (!target.files?.length) return;

  //   file.value = target.files[0];
  //   target.value = "";

  //   if (file.value?.size) {
  //     const calculatedFileSize = Math.round((file.value.size / 1024 / 1024) * 100) / 100;
  //     if (calculatedFileSize > props.fileSize) {
  //       message.warning(`文件大小超过上限${props.fileSize}MB`);
  //       return;
  //     }
  //     fileSize.value = calculatedFileSize;

  //     const selectedFileName = file.value.name;
  //     const fileType = selectedFileName.split(".").pop()?.toLowerCase();

  //     if (!fileType || !props.accept.includes(fileType)) {
  //       message.warning("文件类型不支持");
  //       return;
  //     }

  //     progress.value = 0;
  //     fileName.value = file.value.name;
  //     showFileName.value = true;
  //     totalChunks.value = Math.ceil(file.value.size / chunkSize.value);
  //     objectKey.value = `${getOssDefaultPath(fileName.value, guid())}`;
  //     try {
  //       const res = await request.get(`/file/getUploadId?objectKey=${objectKey.value}`);
  //       uploadId.value = res.data;
  //       progress.value = 1;
  //       uploadFile();
  //     } catch (error) {
  //       message.error("获取上传ID失败");
  //     }
  //   }
}

// 文件上传
async function uploadFile() {
  if (!file.value)
    return

  while (currentChunk.value <= totalChunks.value) {
    // 如果这个分片已经上传成功，跳过
    if (uploadedChunks.value.has(currentChunk.value)) {
      currentChunk.value++
      continue
    }

    const index = currentChunk.value - 1
    const start = index * chunkSize.value
    const end = Math.min((index + 1) * chunkSize.value, file.value.size)

    const formData = new FormData()
    formData.append('file', file.value.slice(start, end))
    formData.append('chunk', String(currentChunk.value))
    formData.append('objectKey', objectKey.value)
    formData.append('uploadId', uploadId.value)

    try {
      uploadStatus.value = 'uploading'
      const res = await request.post('/file/chunk', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        // 添加上传进度监听
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round(
            ((currentChunk.value - 1) / totalChunks.value) * 100
            + (progressEvent.loaded / progressEvent.total!) * (100 / totalChunks.value),
          )
          progress.value = percentCompleted
        },
      })

      // 记录成功上传的分片
      uploadedChunks.value.add(currentChunk.value)
      partArr.value.push(res.data)
      currentChunk.value++
      retryCount.value = 0 // 重置重试计数

      progress.value = Math.floor((currentChunk.value / totalChunks.value) * 100)
    }
    catch (error) {
      console.error('切片上传失败：', error)

      // 重试逻辑
      if (retryCount.value < maxRetries) {
        retryCount.value++
        message.warning(`第 ${currentChunk.value} 个分片上传失败，正在进行第 ${retryCount.value} 次重试...`)
        await new Promise(resolve => setTimeout(resolve, 1000)) // 等待1秒后重试
        continue // 重试当前分片
      }

      // 超过最大重试次数
      uploadStatus.value = 'paused'
      message.error(`上传失败，已暂停。您可以稍后继续上传。`)
      return
    }
  }

  // 所有分片上传完成
  if (currentChunk.value > totalChunks.value) {
    progress.value = 99
    uploadStatus.value = 'completed'
    await complete()
  }
}

// 添加继续上传方法
async function resumeUpload() {
  if (uploadStatus.value === 'paused' && file.value) {
    retryCount.value = 0 // 重置重试计数
    message.info('正在继续上传...')
    await uploadFile()
  }
}

// 完成上传
async function complete() {
  try {
    const res = await request.post(
      `/file/completeUpload?objectKey=${objectKey.value}&uploadId=${uploadId.value}`,
      partArr.value,
    )

    bucketName.value = res.data.bucketName
    objectKeyName.value = res.data.objectKey
    progress.value = 100

    // 发送上传成功事件
    emit('upload-success', {
      objectKey: objectKeyName.value,
      objectUrl: res.data.objectUrl, // 假设后端返回了文件路径
      fileName: fileName.value,
      currentFileSize: currentFileSize.value,
      hashCode: hashCode.value,
    })

    initParam()
  }
  catch (error) {
    console.error('文件上传失败：', error)
    message.warning('文件上传失败')
  }
}

// 取消上传
async function cancelUploadInfo() {
  if (uploadStatus.value !== 'completed') {
    try {
      await request.post('/file/cancelUpload', {
        objectKey: objectKey.value,
        uploadId: uploadId.value,
      })
      uploadStatus.value = 'idle'
      uploadedChunks.value.clear()
      progress.value = 0
      showFileName.value = false
      initParam()
    }
    catch (error) {
      message.error('取消上传失败')
    }
  }
}

// 校验是否已上传
function validRequired() {
  if (bucketName.value && objectKeyName.value) {
    requiredValid.value = false
    return true
  }
  requiredValid.value = true
  return false
}

// 获取上传信息
function getUploadInfo() {
  return {
    bucketName: bucketName.value,
    fileName: objectKeyName.value,
    fileSize: fileSize.value,
  }
}

// 初始化
onMounted(() => {
  progress.value = 0
})

// 明确暴露方法
defineExpose({
  triggerFileSelect,
  validRequired,
  getUploadInfo,
})
</script>

<template>
  <div class="upload-container">
    <div class="fileUpload">
      <div class="item-upload">
        <!-- <span v-if="required" class="required">*</span> -->
        <!-- <n-button type="success" @click="triggerFileSelect"> 文件选择 </n-button> -->
        <input
          ref="fileInputRef"
          type="file"
          class="file2"
          style="display: none"
          @change="selectFile"
        >
      </div>
      <!-- <div v-if="showFileName" class="item-info">
        {{ fileName }}
      </div>
      <div v-else class="item-info">支持{{ getAcceptType() }}文件格式</div>
      <div v-if="progress > 0" class="item-cancel">
        <n-button type="success" style="margin-top: 5px" @click="cancelUploadInfo">
          X
        </n-button>
      </div> -->
    </div>
    <div v-if="progress > 0" class="item-process">
      <n-progress
        type="line"
        :percentage="progress"
        indicator-placement="inside"
        processing
      />
    </div>
    <!-- <div v-if="requiredValid" class="item-valid">请上传文件</div> -->
    <!-- <div class="resume-upload">
      <n-button type="primary" @click="resumeUpload">
        继续上传
      </n-button>
    </div> -->
  </div>
</template>

<style scoped lang="scss">
.upload-container {
  .fileUpload {
    display: flex;
    flex-direction: row;
    padding: 0px 8px;
    .item-upload {
      width: 100px;
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
      .required {
        color: red;
        padding: 0px 5px;
      }
    }
    .item-info {
      flex: 1;
      padding-top: 12px;
      font-size: 13px;
    }
    .item-cancel {
      width: 30px;
      padding-right: 20px;
    }
  }
  .item-process {
    margin-top: 10px;
    /* padding: 0px 23px; */
    box-sizing: border-box;
  }
  .item-valid {
    color: red;
    padding: 0px 10px;
    font-size: 13px;
  }
}
</style>
