/**
 * 封装axios的类
 * nh 2021.12.23
 */

import axios from "axios";
import vueStore from "@/store";
import { logicConfig } from "@/logicConfig";
import { toolUtils } from "@/utils/toolUtils";
import SparkMD5 from "spark-md5";
import {sessionStore} from '@/store/sessionStore'

var CancelToken = axios.CancelToken;
var cancel;

// 创建axios实例
const axiosservice = axios.create({
  timeout: 3000000, // 请求超时时间
  headers: {
    token: sessionStore.userToken
  },
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  }),
});

axiosservice.interceptors.request.use(
  (config) => {
    config.headers['token'] = sessionStore.userToken;
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

axiosservice.interceptors.response.use(
  function (res) {
    //在这里对返回的数据进行处理
    let resp = res.data;
    return res;
  },
  function (err) {
    console.error("axios interceptors err = ", err);
    return Promise.reject(err);
  }
);

export class axiosUtils {
  /**
   * get请求
   * @param url
   * @param data
   * @returns
   */
  protected async getRequest(url: string, data: any) {
    try {
      var urlArr = url.split("?");
      var urlPrefix = urlArr[0];
      var urlParamStr = urlArr[1] || "";

      var _newParamStr = toolUtils.objectKeyToUrlParam(data);

      var newUrl =
        urlPrefix +
        "?" +
        _newParamStr +
        (_newParamStr ? "&" : "") +
        urlParamStr;

      let response = await this.customRequest(newUrl, {}, {}, "get");
      return response;
    } catch (err) {
      throw err;
    }
  }

  /**
   * post请求
   * @param url
   * @param data
   * @returns
   */
  protected async postRequest(url: string, data: any) {
    try {
      let response = await this.customRequest(url, {}, data, "post");
      return response;
    } catch (err) {
      throw err;
    }
  }

  /**
   * 自定义请求
   * @param url
   * @param headers
   * @param data
   * @param method get | post  默认post
   * @returns
   */
  protected async customRequest(
    url: string,
    headers: any,
    data: any,
    method: string = "post"
  ) {
    try {
      data = data || {};

      //headers里放入 groupCode、projectId  为了兼顾老ADM的写法，以后可删除
      headers = headers || {};
      headers.groupCode =
        headers.groupCode || vueStore.state.selectProject.groupCode;
      headers.projectId = headers.projectId || vueStore.state.selectProject.id;

      var urlArr = url.split("?");
      var urlPrefix = urlArr[0];
      var urlParamStr = urlArr[1] || "";
      if (urlParamStr.indexOf("projectId=") == -1) {
        urlParamStr =
          "projectId=" + (data.projectId || vueStore.state.selectProject.id) + "&" + urlParamStr;
      }

      if (urlParamStr.indexOf("groupCode=") == -1) {
        urlParamStr =
          "groupCode=" +
          (data.groupCode || vueStore.state.selectProject.groupCode) +
          "&" +
          urlParamStr;
      }

      if (urlParamStr.indexOf("userId=") == -1) {
        urlParamStr =
          "userId=" +
          (data.userId || vueStore.state.user.id) +
          "&" +
          urlParamStr;
      }

      url = urlPrefix + "?" + urlParamStr;

      let response = await axiosservice({
        url,
        headers: headers || {},
        data,
        method: method == "post" ? "post" : "get",
      });
      return response.data;
    } catch (err) {
      // throw err;
      console.error(err);
      return toolUtils.resultConstructor(
        logicConfig.resultObj.failure,
        null,
        "请求异常," + err.message
      );
    }
  }

  /**
   * 上传文件
   * @param _paramobj 格式如下：
   * {
   *    fileServiceUrl 文件服务地址
   *    uploadProgressCall 上传进度回调函数，函数参数为object，格式如下:{fileName:'文件名称',uploadedTotal:1},uploadedTotal某个文件总共上传的字节数
   *    oneUploadedCall 每上传完一个后的回调函数，函数参数为object，格式如下:{fileName:'文件名称',fileId:'文件ID'}
   *    userId:'不传时，默认会取vuex中user.id',
   *    files 数组或者object，示例
   *    {
   *      groupCode:'不传时，默认会取vuex中selectProject.groupCode',
   *      projectId:'不传时，默认会取vuex中selectProject.id',
   *      fileName:'文件名称',
   *      fileBucket:'存储空间名称，可以不传',
   *      file:文件信息，假设有一个input type为file id为txtFile的标签，那么file为document.getElementById('txtFile').files[0]
   *    }
   * }
   * @returns
   */
  protected async uploadFiles(_paramobj: any) {
    //注意，这里的this 指向的是实例化的子类
    var _this = this;
    let userId = _paramobj.userId || vueStore.state.user.id;
    let fileServiceUrl = _paramobj.fileServiceUrl;
    let files = _paramobj.files;
    try {
      let waitFiles = files instanceof Array == true ? files : [files];
      //上传成功的文件数量
      let uploadedCount = 0;

      for (let i = 0; i < waitFiles.length; i++) {
        let _currFileObj = waitFiles[i];
        //获取文件md5
        let fileContentMd5 = await getFileContentMd5(_currFileObj);
        //获取文件上传地址
        let getUploadResult = await getUploadUrl(
          fileContentMd5 as string,
          _currFileObj
        );
        switch (getUploadResult.result) {
          case logicConfig.resultObj.success:
            let getUploadData = getUploadResult.data || {};
            //uploadCode 200 代表已经上传过了；    201  代表需上传
            switch (getUploadData.uploadCode) {
              case 201:
                //获取文件地址上传成功时进行上传
                let uploadUrl = ((getUploadData.content || [])[0] || {})
                  .uploadUrl;
                if (!uploadUrl)
                  return toolUtils.resultConstructor(
                    logicConfig.resultObj.failure,
                    null,
                    "未获取到文件" + _currFileObj.fileName + "的上传地址"
                  );
                //开始上传
                await upload(
                  uploadUrl,
                  _currFileObj,
                  function (progressObj) {
                    if (typeof _paramobj.uploadProgressCall == "function") {
                      let uploadedFileSize =
                        (progressObj.loaded / progressObj.total) *
                        _currFileObj.file.size;
                      _paramobj.uploadProgressCall({
                        uploadedTotal: uploadedFileSize,
                        fileName: _currFileObj.fileName,
                      });
                    }
                  }
                );
              case 200:
                ++uploadedCount;
                if (typeof _paramobj.oneUploadedCall == "function") {
                  _paramobj.oneUploadedCall({
                    fileName: _currFileObj.fileName,
                    fileId: getUploadData.id,
                  });
                }
                break;
            }
            break;
          default:
            return toolUtils.resultConstructor(
              logicConfig.resultObj.failure,
              null,
              "获取文件" +
              _currFileObj.fileName +
              "的上传地址时出错：" +
              getUploadResult.message
            );
        }
        if (uploadedCount == waitFiles.length)
          return toolUtils.resultConstructor(
            logicConfig.resultObj.success,
            null,
            null
          );
      }
    } catch (error) {
      console.error(error);
      return toolUtils.resultConstructor(
        logicConfig.resultObj.failure,
        null,
        error.message
      );
    }

    //获取文件内容的md5值
    function getFileContentMd5(_fileObj) {
      return new Promise((resolve, reject) => {
        let file = _fileObj.file;
        let blobSlice = File.prototype.slice;
        let chunkSize = 2097152;
        let chunks = Math.ceil(file.size / chunkSize);
        let currentChunk = 0;
        let spark = new SparkMD5.ArrayBuffer();
        let fileReader = new FileReader();
        fileReader.onload = function (e) {
          spark.append(e.target.result); // Append array buffer
          // currentChunk++;
          // if (currentChunk < chunks) {
          //   loadNext();
          // } else {
          //   let fileContentMd5 = spark.end();
          //   console.info("computed hash", fileContentMd5); // Compute hash
          //   //文件md5获取完成后，调用后台接口获取上传地址
          //   // let getUploadResult = await getUploadUrl(fileContentMd5, _fileObj.fileName, file.size);
          //   resolve(fileContentMd5);
          // }
          let fileContentMd5 = spark.end();
          console.info("computed hash", fileContentMd5); // Compute hash
          resolve(fileContentMd5);
        };

        fileReader.onerror = function (e) {
          let errStr = "文件：" + _fileObj.fileName + "读取错误：";
          console.error(e);
          reject(errStr);
        };

        function loadNext() {
          var start = currentChunk * chunkSize,
            end =
              start + chunkSize >= file.size ? file.size : start + chunkSize;

          // fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
          fileReader.readAsArrayBuffer(file);
        }

        loadNext();
      });
    }

    //获取文件上传地址
    async function getUploadUrl(fileContentMd5: string, _fileObj: any) {
      let fileName = _fileObj.fileName;
      let fileSize = _fileObj.file.size;
      let projectId = _fileObj.projectId || vueStore.state.selectProject.id;
      let groupCode =
        _fileObj.groupCode || vueStore.state.selectProject.groupCode;
      //应用ID暂时用不上
      let appId = "";

      let getUploadUrl =
        toolUtils.getBaseHttpUrl(fileServiceUrl, "file/initSingleUpload") +
        "?userId=" +
        userId +
        "&projectId=" +
        projectId +
        "&groupCode=" +
        groupCode +
        "&appId=";

      let paramObj = {
        fileMd5: fileContentMd5,
        fileName: fileName,
        fileBucket: (window as any).__systemConf.baseRouteUrl,
        fileSize: fileSize,
      };
      return await _this.customRequest(getUploadUrl, null, paramObj, "post");
    }

    //上传
    async function upload(uploadUrl, _fileObj, uploadProgressCall) {
      return new Promise((resolve, reject) => {
        let file = _fileObj.file;
        let fileReader = new FileReader();
        fileReader.onload = function (e) {
          axios
            .put(uploadUrl, e.target.result, {
              onUploadProgress: uploadProgressCall,
            })
            .then(function (res) {
              resolve(res);
            })
            .catch(function (error) {
              reject(error);
            });
        };

        fileReader.onerror = function (e) {
          let errStr = "文件：" + _fileObj.fileName + "读取错误：";
          reject(errStr);
        };

        fileReader.readAsArrayBuffer(file);
      });
    }
  }
}
