<template>
  <div class="comp-upload-list">
    <div ref="dragRef" class="btn" @click="openFun" @mousedown="startDragFun">
      <el-icon :class="{ 'icon-animation': uploading }" :size="22"><Upload /></el-icon>
    </div>
    <el-drawer title="上传列表" v-model="listVisible" size="400" :modal="false" modal-penetrable>
      <div class="content">
        <div v-if="listData.length > 0" class="list">
          <div v-for="(item, index) in listData" :key="item.file.name" class="item">
            <div class="main">
              <div class="toper">
                <div class="name">
                  {{ item.file.name }}
                </div>
                <div class="status">
                  {{ UPLOAD_FILE_STATUS[item.data.status] }}
                </div>
              </div>
              <div class="progress">
                <el-progress
                  :show-text="false"
                  :percentage="Number((item.data.current / item.data.total) * 100) || 0"
                  :status="item.data.status === '-1' ? 'exception' : ''"
                  :stroke-width="5"
                />
              </div>
              <div class="info">
                <div class="speed">{{ item.data.speed || '--' }}/s</div>
                <div class="size">
                  {{
                    formatFileSize((item.data.current / item.data.total) * item.file.size || 0) ||
                    '--'
                  }}/{{ formatFileSize(item.file.size) }}
                </div>
              </div>
            </div>
            <div class="options">
              <el-icon v-if="item.data.status !== '1'" :size="16" @click="removeFun(index)"
                ><Delete
              /></el-icon>
            </div>
          </div>
        </div>
        <div v-else class="no-task">暂无上传任务</div>
      </div>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { Upload, Delete } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { formatFileSize } from '@/utils/file';
import { chunkUploadToMinioApi, chunkUploadCallbackApi } from '@/api/data/data';
import emitter from '@/utils/eventBus';

const taskStatusObj: any = {}; // 更具uploadTaskId和businessId记录所有任务文件的上传状态
const listVisible = ref(false);
const listData = ref<any>([]);
const chunkSize = 1024 * 1024 * 10; // 每片10MB
const uploading = ref(false);
const UPLOAD_FILE_STATUS: any = {
  // TODO
  '-1': '上传失败',
  '0': '待上传',
  '1': '上传中',
  '2': '上传完成',
};

const dragRef = ref<any>();
let msgTimeOut: any = null;
let isDragging = false;
let isDragged = false;
let offsetX = 0;
let offsetY = 0;

emitter.on('ADD_UPLOAD_FILE', (addData: any) => {
  const data = addData.data;
  data.status = '0'; // 默认状态
  if (!taskStatusObj[data.uploadTaskId]) {
    taskStatusObj[data.uploadTaskId] = {};
  }
  taskStatusObj[data.uploadTaskId][data.businessId] = data.status;
  initFun(addData);
});

onMounted(() => {
  initDragFun();
});

const initFun = (data: any) => {
  listData.value.push(data);
  if (uploading.value) {
    return;
  }
  listData.value.some((item: any, index: number) => {
    if (item.data.status === '0') {
      sliceFileFun(index);
      return true;
    }
  });
};

// 分片并上传
const sliceFileFun = async (fileIndex: number) => {
  uploading.value = true;
  const fileObj = listData.value[fileIndex];
  const file = fileObj.file;
  const fileData = fileObj.data;
  const totalChunks = Math.ceil(file.size / chunkSize);
  let fileTempPath = '';
  // ElMessage.success(`${file.name} 开始上传`);
  for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
    const start = chunkIndex * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunkFile = file.slice(start, end);
    const params: any = {
      uploadTaskId: fileData.uploadTaskId,
      businessId: fileData.businessId,
      objectKey: fileData.objectKey,
      sourceFileName: fileData.sourceFileName,
      chunk: chunkIndex + 1,
      chunkTotal: totalChunks,
      file: chunkFile,
      isSaveLocal: fileData.isSaveLocal,
    };
    if (fileTempPath) {
      params.fileTempPath = fileTempPath;
    }
    const res: any = await uploadFun(params);
    if (res.result) {
      fileTempPath = res.fileTempPath;
      fileObj.data.total = totalChunks;
      fileObj.data.current = chunkIndex + 1;
      fileObj.data.speed = res.speed;
      fileObj.data.status = chunkIndex + 1 === totalChunks ? '2' : '1';
    } else {
      fileObj.data.status = '-1';
    }
    taskStatusObj[fileObj.data.uploadTaskId][fileObj.data.businessId] = fileObj.data.status;
    if (fileObj.data.status === '2') {
      emitter.emit('UPLOAD_FINISHED', {
        callbackFlag: fileObj.data.callbackFlag,
      });
      if (msgTimeOut) {
        clearTimeout(msgTimeOut);
        msgTimeOut = null;
      }
      msgTimeOut = setTimeout(() => {
        ElMessage.success(`${file.name} 上传成功`);
      }, 3000);
    }
    callBackFun(fileData);
  }
  setTimeout(() => {
    uploading.value = false;
    fileTempPath = '';
    const index = listData.value.findIndex((item: any) => item.data.status === '0');
    if (listData.value[index]) {
      sliceFileFun(index);
    }
  }, 2000); // 2s后自动开启下次上传，没有具体作用
};

// 后处理回调校验
const callBackFun = (data: any) => {
  const { uploadTaskId, isApprove, taskType } = data;
  const totalTaskNum = Object.keys(taskStatusObj[uploadTaskId]).length;
  const finishTaskNum = Object.values(taskStatusObj[uploadTaskId]).filter(
    (val) => val === '2' || val === '-1'
  ).length;
  const successData = Object.entries(taskStatusObj[uploadTaskId])
    .filter(([, value]) => value === '2')
    .map(([key]) => key);
  const failData = Object.entries(taskStatusObj[uploadTaskId])
    .filter(([, value]) => value === '-1')
    .map(([key]) => key);
  if (finishTaskNum === totalTaskNum) {
    const params = {
      uploadTaskId: uploadTaskId,
      succBusinessIds: successData,
      failBusinessIds: failData,
      isApprove,
    };
    const apiObj: any = {
      1: chunkUploadCallbackApi, // 数据总览后处理
      2: chunkUploadCallbackApi, // 知识库后处理
      4: chunkUploadCallbackApi, // 交付物后处理
      5: chunkUploadCallbackApi, // 项目文件接口后处理
    };
    apiObj[taskType](params);
  }
};

const uploadFun = async (params: any) => {
  const starTime = new Date().getTime();
  const res: any = await chunkUploadToMinioApi(params);
  const data = res.data || {};
  const endTime = new Date().getTime();
  data.speed = formatFileSize(chunkSize / ((endTime - starTime) / 1000)); // 根据每次分片上传时间大致算一个速度
  return data;
};

const openFun = () => {
  if (isDragged) {
    isDragged = false;
    return;
  }
  listVisible.value = true;
};

const removeFun = (index: any) => {
  listData.value.splice(index, 1);
};

const startDragFun = (e: any) => {
  isDragging = true;
  const rect = dragRef.value.getBoundingClientRect();
  offsetX = e.clientX - rect.left;
  offsetY = e.clientY - rect.top;
  document.addEventListener('mousemove', onDragFun);
  document.addEventListener('mouseup', stopDragFun);
};
const onDragFun = (e: any) => {
  if (!isDragging) {
    return;
  }
  const el = dragRef.value;
  const width = el.offsetWidth;
  const height = el.offsetHeight;
  let x = e.clientX - offsetX;
  let y = e.clientY - offsetY;
  isDragged = true;
  const maxX = document.documentElement.clientWidth - width;
  const maxY = document.documentElement.clientHeight - height;
  x = Math.max(0, Math.min(x, maxX));
  y = Math.max(0, Math.min(y, maxY));
  if (x <= 0) {
    x = 5;
    isDragging = false;
  }
  if (x >= maxX) {
    x = maxX - 5;
    isDragging = false;
  }
  if (y <= 0) {
    y = 5;
    isDragging = false;
  }
  if (y >= maxY) {
    y = maxY - 5;
    isDragging = false;
  }
  el.style.left = x + 'px';
  el.style.top = y + 'px';
  localStorage.setItem('UPLOAD_LIST_X', String(x));
  localStorage.setItem('UPLOAD_LIST_Y', String(y));
};
const stopDragFun = () => {
  setTimeout(() => {
    isDragging = false;
  }, 200);
  document.removeEventListener('mousemove', onDragFun);
  document.removeEventListener('mouseup', stopDragFun);
};
const initDragFun = () => {
  const x = localStorage.getItem('UPLOAD_LIST_X');
  const y = localStorage.getItem('UPLOAD_LIST_Y');
  if (x && y) {
    const el = dragRef.value;
    el.style.left = x + 'px';
    el.style.top = y + 'px';
  }
};
</script>

<style lang="scss" scoped>
@keyframes moveUp {
  0% {
    transform: translateY(0);
  }
  49% {
    transform: translateY(-30px);
  }
  50% {
    transform: translateY(30px);
  }
  100% {
    transform: translateY(0);
  }
}
.comp-upload-list {
  .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    top: 50%;
    right: 20px;
    width: 40px;
    height: 40px;
    background-color: var(--el-color-primary);
    box-shadow: 0 0 10px var(--el-text-color-primary);
    border-radius: 50%;
    z-index: 9999;
    color: var(--el-bg-color);
    cursor: pointer;
    user-select: none;
    overflow: hidden;
    .icon-animation {
      animation: moveUp 1s infinite;
    }
  }
  .content {
    height: 100%;
    .list {
      .item {
        display: flex;
        border-bottom: solid 1px var(--el-border-color);
        padding: 10px 0;
        &:last-child {
          border-bottom: 0;
        }
        .main {
          padding: 0 10px;
          flex: 1;
          .toper {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding-bottom: 4px;
            line-height: 14px;
            font-size: 12px;
            .name {
              flex: 1;
              color: var(--el-text-color-primary);
              word-break: break-word;
            }
            .status {
              color: var(--el-text-color-secondary);
            }
          }
          .progress {
            height: 5px;
          }
          .info {
            display: flex;
            align-items: center;
            justify-content: space-between;
            font-size: 12px;
            padding-top: 4px;
            color: var(--el-text-color-secondary);
          }
        }
        .options {
          width: 20px;
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--el-color-danger);
          cursor: pointer;
        }
      }
    }
    .no-task {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--el-text-color-placeholder);
      font-size: 14px;
    }
  }
}
</style>
