"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
require("core-js/modules/es.promise.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
var _react = _interopRequireDefault(require("react"));
var _utils = require("antd/es/upload/utils");
var _cosJsSdkV = _interopRequireDefault(require("cos-js-sdk-v5"));
var _lodash = require("lodash");
var _useFreshState = _interopRequireDefault(require("./useFreshState"));
var _upload = _interopRequireDefault(require("../upload"));
var _request = _interopRequireDefault(require("./utils/request"));
var _eachLimit = require("./utils/eachLimit");
var _jsxRuntime = require("react/jsx-runtime");
/* eslint-disable no-async-promise-executor */

// cos 基础上传
const cosBasicRegister = async ({
  file,
  item
}, onProgress, cosApiParams) => {
  return new Promise(async (resolve, reject) => {
    try {
      const cos = new _cosJsSdkV.default({
        SecretId: item.secretId,
        SecretKey: item.secretKey,
        XCosSecurityToken: item.sessionToken
      });

      // 直接存储到 cos 服务
      const cosPutData = await cos.putObject({
        Bucket: item.bucket,
        Region: item.region,
        Key: item.storeName,
        Body: file.originFileObj,
        onProgress: (progressData = {}) => {
          const {
            percent
          } = progressData;
          onProgress({
            percent
          }, file);
        },
        ...(cosApiParams === null || cosApiParams === void 0 ? void 0 : cosApiParams.putObject)
      });
      resolve(cosPutData);
    } catch (error) {
      reject(error);
    }
  });
};

// cos 分片上传
const cosMultipartRegister = async ({
  cosMultipartChunkSize,
  cosApiParams
}, {
  file,
  item
}, onProgress) => {
  return new Promise(async (resolve, reject) => {
    // 分片内容大小
    const ChunkSize = cosMultipartChunkSize || 1024 * 1024 * 8;
    // 获取分片数量
    const Parts = new Array(Math.ceil((file.size || 0) / ChunkSize)).fill(0).map((_pIem, pIndex) => {
      return {
        PartNumber: pIndex + 1
      };
    });
    const cos = new _cosJsSdkV.default({
      SecretId: item.secretId,
      SecretKey: item.secretKey,
      XCosSecurityToken: item.sessionToken
    });
    let cosMultipartInitRes = {};
    try {
      // 分片初始化
      cosMultipartInitRes = await cos.multipartInit({
        Bucket: item.bucket,
        Region: item.region,
        Key: item.storeName,
        ...(cosApiParams === null || cosApiParams === void 0 ? void 0 : cosApiParams.multipartInit)
      });

      // 完成分片的进度
      let successUploadIndex = 0;
      const syncTaskLimit = 3; // 最多同时执行几个任务

      (0, _eachLimit.eachLimit)(Parts, syncTaskLimit, (partItem, nextPart) => {
        var _file$originFileObj;
        const PartNumber = partItem.PartNumber;
        const start = (PartNumber - 1) * ChunkSize;
        const end = Math.min(start + ChunkSize);
        const blob = file === null || file === void 0 || (_file$originFileObj = file.originFileObj) === null || _file$originFileObj === void 0 ? void 0 : _file$originFileObj.slice(start, end);

        // 上传每个分片
        cos.multipartUpload({
          Bucket: item.bucket,
          Region: item.region,
          Key: item.storeName,
          UploadId: cosMultipartInitRes.UploadId,
          PartNumber,
          Body: blob,
          ...(cosApiParams === null || cosApiParams === void 0 ? void 0 : cosApiParams.multipartUpload)
        }, (err, data) => {
          var _data$headers;
          if (err) return nextPart(err);
          if (!(data !== null && data !== void 0 && (_data$headers = data.headers) !== null && _data$headers !== void 0 && _data$headers.etag)) {
            return nextPart('浏览器获取不到 ETag Header，需要存储桶配置 CORS ExposeHeaders 允许当前域名跨域读取 ETag 字段。');
          }
          partItem.ETag = data.headers.etag || '';
          successUploadIndex++;

          // console.log('分片上传已完成：', successUploadIndex, partItem.PartNumber, partItem.ETag);
          onProgress({
            percent: Math.floor(100 / Parts.length * successUploadIndex)
          }, file);
          nextPart();
        });
      }, (err, data) => {
        // console.log('end multipartUpload', err, data);
        if (err) {
          // console.error('上传分片出错：', err);
          reject(err);
        } else {
          // 完成分片上传
          cos.multipartComplete({
            Bucket: item.bucket,
            Region: item.region,
            Key: item.storeName,
            UploadId: cosMultipartInitRes.UploadId,
            Parts: data,
            ...(cosApiParams === null || cosApiParams === void 0 ? void 0 : cosApiParams.multipartComplete)
          }, (comErr, comData) => {
            if (comErr) {
              reject(comErr);
            }
            resolve(comData);
          });
        }
      });
    } catch (error) {
      // 如果错误，则及时清空分片数据
      await cos.multipartAbort({
        Bucket: item.bucket,
        Region: item.region,
        Key: item.storeName,
        UploadId: cosMultipartInitRes.UploadId,
        ...(cosApiParams === null || cosApiParams === void 0 ? void 0 : cosApiParams.multipartAbort)
      });
      reject(error);
    }
  });
};
const InternalUpload = (props, ref) => {
  const {
    fileList: fileListProp,
    defaultFileList,
    children,
    materialType,
    cosUpload,
    cosUploadType: cosUploadTypeProp,
    cosApiParams,
    cosRequestUrl,
    vodRequestUrl,
    vodCommitRequestUrl,
    requestConfigData,
    cosRequestMethod,
    vodCommitRequestMethod,
    maxCount,
    onChange,
    onError,
    onRemove,
    ...userProps
  } = props;
  const upload = _react.default.useRef();
  const onInternalChange = (info, emit = false) => {
    let cloneList = [...info.fileList];

    // Cut to match count
    if (maxCount === 1) {
      cloneList = cloneList.slice(-1);
    } else if (maxCount) {
      cloneList = cloneList.slice(0, maxCount);
    }
    setFileList((0, _lodash.cloneDeep)(cloneList));

    // console.log('onInternalChange', info.file.percent);

    if (emit) {
      onChange === null || onChange === void 0 ? void 0 : onChange({
        ...info,
        fileList: cloneList
      });
    }
  };
  const onStart = file => {
    const targetItem = (0, _utils.file2Obj)(file);
    targetItem.status = 'uploading';
    const nextFileList = getFileList().concat();
    const fileIndex = nextFileList.findIndex(({
      uid
    }) => uid === targetItem.uid);
    if (fileIndex === -1) {
      nextFileList.push(targetItem);
    } else {
      nextFileList[fileIndex] = targetItem;
    }
    onInternalChange({
      file: targetItem,
      fileList: nextFileList
    }, true);
  };
  const onSuccess = (response, file, xhr) => {
    try {
      if (typeof response === 'string') {
        response = JSON.parse(response);
      }
    } catch (e) {
      /* do nothing */
    }
    const targetItem = (0, _utils.getFileItem)(file, getFileList());
    // removed
    if (!targetItem) {
      return;
    }
    targetItem.status = 'done';
    targetItem.response = response;
    targetItem.xhr = xhr;
    targetItem.url = file.url;
    onInternalChange({
      file: {
        ...targetItem
      },
      fileList: getFileList().concat()
    }, true);
  };
  const onProgress = (e, file) => {
    const targetItem = (0, _utils.getFileItem)(file, getFileList());
    // removed
    if (!targetItem) {
      return;
    }
    targetItem.status = 'uploading';
    targetItem.percent = e.percent;
    onInternalChange({
      event: e,
      file: {
        ...targetItem
      },
      fileList: getFileList().concat()
    });
  };
  const onInternalError = (error, response, file) => {
    const targetItem = (0, _utils.getFileItem)(file, getFileList());
    // removed
    if (!targetItem) {
      return;
    }
    targetItem.error = error;
    targetItem.response = response;
    targetItem.status = 'error';
    onInternalChange({
      file: {
        ...targetItem
      },
      fileList: getFileList().concat()
    });

    // 新增 onError 回调
    onError === null || onError === void 0 ? void 0 : onError(file, error);
  };
  const [getFileList, setFileList] = (0, _useFreshState.default)(fileListProp || defaultFileList || [], fileListProp);
  const clearFileList = () => {
    // console.log('start clearFileList');
    setFileList([]);
  };
  const beforeUpload = (cbFile, cbFileList) => {
    // console.log('beforeUpload');
    const {
      beforeUpload: beforeUploadProp
    } = props;

    // 上传逻辑函数
    const uploadFunc = async () => {
      if (cosUpload) {
        onStart(cbFile);

        // 将 fileList 转换为 map 结构
        const fileListMap = {};
        const cosRequestData = [cbFile].map(item => {
          fileListMap[item.uid] = item;
          return {
            fileUid: item.uid,
            fileSize: item.size,
            fileName: item.name,
            fileType: item.type
          };
        });
        try {
          let cosResponse;

          // 阶段一
          if (cosRequestMethod) {
            cosResponse = await cosRequestMethod([cbFile]);
          } else {
            // 如果没有指定上传函数，则用内置的 request 函数
            let requestUrl = '';
            if (materialType === 'image' && cosRequestUrl) {
              requestUrl = cosRequestUrl;
            } else if (materialType === 'video' && vodRequestUrl) {
              requestUrl = vodRequestUrl;
            } else {
              return Promise.reject(new Error('缺少上传相关配置参数 cosRequestMethod | cosRequestUrl | vodRequestUrl'));
            }
            cosResponse = await (0, _request.default)(requestUrl, {
              ...requestConfigData,
              method: 'POST',
              data: {
                data: cosRequestData
              }
            });
          }
          if (!cosResponse || !Array.isArray(cosResponse)) {
            return Promise.reject(new Error('cosRequestUrl 返回值有误 '));
          }
          const cosSuccessRes = await Promise.all(cosResponse.map(async item => {
            const fileObj = (0, _utils.file2Obj)(fileListMap[item.fileUid]);
            const targetItem = (0, _utils.getFileItem)(fileObj, getFileList());
            let cosResData = {};
            if (cosUploadTypeProp === 'basic') {
              cosResData = await cosBasicRegister({
                file: targetItem,
                item
              }, onProgress, cosApiParams);
            } else if (cosUploadTypeProp === 'multipart') {
              cosResData = await cosMultipartRegister(props, {
                file: targetItem,
                item
              }, onProgress);
            }
            const resultObj = {
              name: cbFile.name,
              response: cosResData,
              url: cosResData.Location
            };
            if (materialType === 'image') {
              // 处理 COS 服务返回的资源地址少了 https
              const currentCosReqItem = cosResponse.find(i => i.url.indexOf(cosResData.Location) >= 0);
              if (currentCosReqItem) {
                resultObj.url = currentCosReqItem.url;
              }
            }

            // 阶段二
            if (materialType === 'video' && item.vodSessionKey) {
              var _videoCommitRes;
              let videoCommitRes;
              if (vodCommitRequestMethod) {
                videoCommitRes = await vodCommitRequestMethod({
                  vodSessionKey: item.vodSessionKey
                });
              } else {
                if (!vodCommitRequestUrl) {
                  Promise.reject(new Error('当前上传接口类型 props.materialType 为 video，但是没有指定 props.vodCommitRequestUrl'));
                }

                // Vod commit
                videoCommitRes = await (0, _request.default)(vodCommitRequestUrl, {
                  ...requestConfigData,
                  method: 'POST',
                  data: {
                    vodSessionKey: item.vodSessionKey
                  }
                });
              }

              // 如果调起了 VOD 服务，则修改为 VOD 返回值
              resultObj.response = videoCommitRes;
              resultObj.url = (_videoCommitRes = videoCommitRes) === null || _videoCommitRes === void 0 ? void 0 : _videoCommitRes.mediaUrl;
            }
            resultObj._uid = item.fileUid;
            return resultObj;
          }));
          const newFileList = cosSuccessRes.map(item => {
            return {
              uid: item._uid,
              name: item.name,
              url: item.url,
              thumbUrl: item.url,
              response: item.response
            };
          });

          // 更新 loading 效果仅更新第一条数据，每次只上传一个文件，保证 loading 效果独立
          const currentItem = newFileList[0];
          onProgress({
            percent: 100
          }, currentItem);
          onSuccess(currentItem.response, currentItem, currentItem);
        } catch (error) {
          onInternalError(error, error, cbFile);
        }
      } else {
        onSuccess({}, cbFile, {});
      }
    };
    if (beforeUploadProp) {
      const result = beforeUploadProp(cbFile, cbFileList);

      // 或者 promise 为 resolve 则都调用上传功能
      if (result && result.then) {
        Promise.resolve(result).then(res => {
          uploadFunc();
          return Promise.resolve(res);
        }).catch(err => {
          return Promise.reject(err);
        });
      } else if (typeof result === 'boolean' && result) {
        uploadFunc();
      } else {
        return Promise.reject(new Error('function return error!'));
      }
    } else if (typeof beforeUploadProp === 'undefined') {
      uploadFunc();
    }
    return Promise.reject(new Error('cancel normal upload step.'));
  };
  const handleRemove = file => {
    let currentFile;
    Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => {
      // Prevent removing file
      if (ret === false) {
        return;
      }
      const fileList = getFileList();
      const removedFileList = (0, _utils.removeFileItem)(file, fileList);
      if (removedFileList) {
        currentFile = {
          ...file
        };
        fileList === null || fileList === void 0 ? void 0 : fileList.some(item => {
          const matchKey = currentFile.uid !== undefined ? 'uid' : 'name';
          if (item[matchKey] === currentFile[matchKey]) {
            return true;
          }
          return false;
        });
        onInternalChange({
          file: currentFile,
          fileList: removedFileList
        }, true);
      }
    });
  };

  // 将内部属性抛给 ref 实例对象
  _react.default.useImperativeHandle(ref, () => ({
    fileList: getFileList(),
    upload: upload.current,
    clearFileList
  }));
  const _uploadProps = {
    ...userProps,
    fileList: getFileList(),
    beforeUpload,
    onRemove: handleRemove,
    onChange: onInternalChange
  };
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_upload.default, {
    ref: upload,
    ..._uploadProps,
    children: children
  });
};
const WauUpload = /*#__PURE__*/_react.default.forwardRef(InternalUpload);
WauUpload.displayName = 'WauUpload';
WauUpload.defaultProps = {
  materialType: 'image',
  cosUpload: false,
  cosUploadType: 'basic',
  cosMultipartChunkSize: 10 * 1024 * 1024,
  cosRequestUrl: '',
  vodRequestUrl: '',
  vodCommitRequestUrl: ''
};
var _default = WauUpload;
exports.default = _default;