import { executePromiseArray } from "./../utils/promise";
import { blobToBase64 } from "@/utils/file";
import { ElMessage } from "element-plus";
import { $ajax } from ".";
import { sha256 } from "js-sha256";
import axios, { Canceler } from "axios";
import type { getFileHash } from "@/worker";
import Worker from "@/worker?worker";
import { wrap } from "comlink";

// 分块文件检查
export function fileCheck(query: any) {
  return $ajax({
    url: "/api/file/chunk/check",
    type: "get",
    params: query,
  });
}
// 文件合并
export function fileMerge(data: any) {
  return $ajax({
    url: "/api/file/chunk/merge",
    type: "post",
    params: data,
  });
}

export function fileUnzip(data: {
  category_id: number;
  file_name: string;
  file_hash: string;
}) {
  return $ajax({
    url: "/api/file/chunk/unzip",
    type: "post",
    params: data,
  });
}

var CancelToken = axios.CancelToken;

// 分块文件上传
export function fileUpload(
  data: any,
  onProgressChange?: (progressEvent: {
    loaded: number;
    total: number;
    [key: string]: any;
  }) => void
) {
  const source = CancelToken.source();
  const res = $ajax({
    url: "/api/file/chunk/upload",
    type: "post",
    params: data,
    config: {
      cancelToken: source.token,
      onUploadProgress: onProgressChange,
    },
  });
  return Object.assign(res, { cancel: source.cancel });
}
// 批量获取文件
export function getFiles(query: any) {
  return $ajax({
    url: `/api/files?${query}`,
    type: "get",
  });
}

interface iFileChunkPayload {
  current_data: string; //当前块数据
  current_seq: number; // 当前块序号
  current_size: number; //当前块大小
  file_name: string; //文件名称
  file_hash: string; //文件hash
  total_seq: number; //总序号
  total_size: number; //总大小
}

async function worker_getFileHash(file: File) {
  const workerGetFileHash = wrap<typeof getFileHash>(new Worker());
  return workerGetFileHash(file);
}

function splitFileToFileChunks(file: File, chunkSize = 16) {
  const fileChunks: Blob[] = [];
  let slicesize = file.size;
  while (slicesize > chunkSize * 1024 * 1024) {
    const blobPart = file.slice(
      fileChunks.length * chunkSize * 1024 * 1024,
      (fileChunks.length + 1) * chunkSize * 1024 * 1024
    );
    fileChunks.push(blobPart);
    slicesize -= chunkSize * 1024 * 1024;
  }
  if (slicesize > 0) {
    fileChunks.push(
      file.slice(fileChunks.length * chunkSize * 1024 * 1024, file.size)
    );
  }
  return fileChunks;
}

export function handleBigFileUpload(
  file: File,
  onPercentCompleteChange?: (percent: number) => void,
  onDown?: any
) {
  return new Promise<{
    unzip: (category_id: number) => Promise<any>;
    uploadAllFileChunks: Promise<boolean>;
    cancelUpload?: () => void;
  }>(async (rootResolve, rootReject) => {
    let isCancel = false;
    let __total = 0;
    const cancelTokenList: Canceler[] = [];
    let progressLoaded = 0;
    const FILE_SIZE = file.size;

    const fileChunks = splitFileToFileChunks(file);

    const fileHash = await worker_getFileHash(file);
    fileCheck({ file_hash: fileHash }).then((res: any) => {
      if (!res) return;
      let { is_done, file_info } = res.data;
      if (is_done && file_info) {
        // 上传完成
        ElMessage({
          message: "上传成功",
          type: "success",
        });
        rootResolve({
          unzip: (category_id: number) =>
            fileUnzip({
              category_id,
              file_hash: res.data.file_hash,
              file_name: file_info.name,
            }),
          uploadAllFileChunks: Promise.resolve(true),
        });
        return;
      }

      const uploadAllFileChunks = new Promise<boolean>(
        (resolveAllFileUpload, rejectAllFileUpload) => {
          function uploadFileChunk(
            fileChunkPayload: iFileChunkPayload,
            numOfFileChunks: number
          ) {
            return new Promise((resolveFileChunkUpload) => {
              if (isCancel) {
                return;
              }
              let lastLoaded = 0;
              const afterFileUpload = fileUpload(
                fileChunkPayload,
                (progressEvent) => {
                  progressLoaded -= lastLoaded;
                  lastLoaded =
                    (progressEvent.loaded / progressEvent.total) *
                    fileChunkPayload.current_size;
                  progressLoaded +=
                    (progressEvent.loaded / progressEvent.total) *
                    fileChunkPayload.current_size;

                  console.log(progressLoaded / FILE_SIZE);
                  onPercentCompleteChange &&
                    onPercentCompleteChange(progressLoaded / FILE_SIZE);
                }
              );
              cancelTokenList.push(afterFileUpload.cancel);
              afterFileUpload.then((res) => {
                if (!res && !isCancel) {
                  ElMessage({
                    message: "上传失败，请重新上传",
                    type: "warning",
                  });
                  rejectAllFileUpload("上传失败，请重新上传");
                  return;
                }
                __total += 1;
                if (__total === fileChunks.length) {
                  onPercentCompleteChange && onPercentCompleteChange(1);
                  onDown();
                  console.log(1, "上传完成");
                }
                if (__total === numOfFileChunks) {
                  resolveAllFileUpload(true);
                }
                resolveFileChunkUpload(true);
              });
            });
          }

          const promiseFnList = fileChunks.map((fileChunk, index) => {
            return () =>
              blobToBase64(fileChunk).then((base64FileData: string) => {
                return uploadFileChunk(
                  {
                    current_data: base64FileData, //当前块数据
                    current_seq: index + 1, // 当前块序号
                    current_size: fileChunk.size, //当前块大小
                    file_name: file.name, //文件名称
                    file_hash: fileHash, //文件hash
                    total_seq: fileChunks.length, //总序号
                    total_size: file.size, //总大小
                  },
                  fileChunks.length
                );
              });
          });
          executePromiseArray(promiseFnList);
        }
      );

      rootResolve({
        uploadAllFileChunks,
        unzip: (category_id: number) => {
          return uploadAllFileChunks.then(() => {
            return fileUnzip({
              category_id,
              file_hash: fileHash,
              file_name: file.name,
            });
          });
        },
        cancelUpload: () => {
          isCancel = true;
          cancelTokenList.forEach((cancel) => cancel());
        },
      });
    });
  });
}
