import React from 'react';
import { Modal } from 'antd';
import _ from 'underscore';

import { getEllipsText, randomString } from "@/domains/basic-domain/utils";
import {
  DEFAULT_SIZE_UNIT,
  FILE_TYPE_ICON_MAP,
  FILR_VERIFY_MAP,
  UPLOAD_PART_SIZE,
  UPLOAD_PARALLEL,
  TX_UPLOAD_REGION,
} from "@/domains/resource-disk/constants";
import { getFileTypeByName } from '@/domains/resource-disk/utils';
import UploadFileService from '@/domains/resource-disk/UploadFileService';  
import User from '@/common/js/user';
import './UploadProgressModal.less';


class UploadProgressModal extends React.Component {
  constructor(props)  {
    super(props);
    this.state = {
      fileList: [],
      showFileList: true,    // 默认展开上传进度  
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.isOpen && !_.isEqual(nextProps.fileList, this.props.fileList)) {
      const { fileList } = nextProps;
      const _fileList = fileList.map((file) => {
        return {
          fileContent: file,
          status: 'waiting',
          checkpoints: {},
        }
      });

      const allFileList = [...this.state.fileList, ..._fileList];
      this.setState({
        fileList: allFileList
      }, () => {
        // 构建上传表单
        this.handleConstructForm(_fileList);
      })
    }
  }

  // 遍历上传文件，给每个文件构建上传表单
  handleConstructForm = (newFileList) => {
    const { fileList } = this.state;
    const length = fileList.length;
    const newLength = newFileList.length;
    const startI = length === newLength ? 0 : length - newLength;

    // 只给新添加的文件构建上传表单
    for (let i = startI; i < length; i++) {
      this.handleStartUpload(i);
    }
  }

  // 初始化ossClient
  initOssClient = (id, resourceName) => {
    const instId  = User.getStoreId();;

    return new Promise(resolve => {
      // 获取STS Token
      UploadFileService.getOssClient({
        instId,
        resourceName,
        data: { folderId: id  },
        accessTypeEnum: 'PUBLIC',
        bizCode: 'UPLOAD_FOLDER',
      }).then((result) => {
        const { ossClient, resourceId, ossUri, vendorType,bucket,callbackData,callback} = result;
         this.setState({
            ossClient,
            resourceId,
            callbackData,
            callback,
            ossUri,
            bucket,
            vendorType
          });
        resolve({ ossClient, resourceId, vendorType, bucket, callbackData, callback, ossUri });
      })
    })
  }

  // 开始分片上传
  handleStartUpload = (index) => {
    const { currentFolder } = this.props;
    const { fileList } = this.state;
    const file = fileList[index];
    const { fileContent } = file;
    const { name, type } = fileContent;
    const resourceName = randomString(16) + name.slice(name.lastIndexOf('.'));
    const { id = 0 } = currentFolder;
  
    // 开始上传之前初始化OssClient
    this.initOssClient(id, resourceName).then((result) => {
       const { ossClient, resourceId, ossUri, vendorType,bucket,callbackData,callback} = result;
      if (vendorType === 'TENCENT_COS') {
        // 腾讯云分片上传
        ossClient.sliceUploadFile({
          Bucket: bucket,
          Region: TX_UPLOAD_REGION,
          Key:ossUri,
          Body: fileContent,
          SliceSize: UPLOAD_PART_SIZE,
          // 回调配置
          Callback: callback,
          onProgress: (progressData) => {
            const progress = progressData.percent;
            this.onMultipartUploadProgress(progress, { uploadId: randomString(16), file: fileContent }, file);
          },
          onTaskReady: (taskId) => {
            // 记录任务ID，用于取消上传
            file.taskId = taskId;
          },
        })
        .then(res => {
          file.status = 'success';
          this.setState({ fileList });
          this.props.onUpload(fileContent, resourceId);
        })
        .catch(err => {
          file.status = 'fail';
          this.setState({ fileList });
        });
      } else {
        // 阿里云分片上传
        ossClient
          .multipartUpload(ossUri, fileContent, {
            callback: {
              url: callbackData.url,
              body: callbackData.body,
              callbackSNI: true,
              contentType: callbackData.contentType
            },
            parallel: UPLOAD_PARALLEL,
            partSize: UPLOAD_PART_SIZE,
            progress: (progress, checkpoint) => {
              this.onMultipartUploadProgress(progress, checkpoint, file);
            },
            mime: type
          })
          .then(url => {
            file.status = 'success';
            this.setState({ fileList });
            this.props.onUpload(fileContent, resourceId);
          })
          .catch(err => {
            file.status = 'fail';
            this.setState({ fileList });
          });
      }
    });
  }

  // 给每个文件设置进度显示
  onMultipartUploadProgress = (progress = 0, checkpoint, file) => {
    const { fileList } = this.state;
    file.status = 'uploading';
    this.setState({ fileList })
    file.progress = (progress * 100).toFixed(2);
    
    // 如果文件小于100KB，不用记录断点，直接上传
    const { fileContent } = file;
    const { size } = fileContent;
    if (size > 100 * 1024 && checkpoint && checkpoint.uploadId) {
      // 确保checkpoint和uploadId存在时才记录
      if (!file.checkpoints) {
        file.checkpoints = {};
      }
      file.checkpoints[checkpoint.uploadId] = checkpoint;
    }
  }

  // 断点续传
  handleReUpload = (index) => {
    const { fileList, bucket,ossUri, ossClient, callback, resourceId, callbackData, vendorType } = this.state;
    const currentFile = fileList[index];
    const { checkpoints, fileContent } = currentFile;
    if ( vendorType === 'TENCENT_COS') {
      // 腾讯云重新上传
      ossClient.sliceUploadFile({
        Bucket: bucket,
        Region: TX_UPLOAD_REGION,
        Key: ossUri,
        Body: fileContent,
        SliceSize: UPLOAD_PART_SIZE,
        // 回调配置
        Callback: callback,
        onProgress: (progressData) => {
          const progress = progressData.percent;
          this.onMultipartUploadProgress(progress, { uploadId: uuid(), file: fileContent }, currentFile);
        },
        onTaskReady: (taskId) => {
          currentFile.taskId = taskId;
        },

      })
        .then(res => {
          currentFile.status = 'success';
          this.setState({ fileList });
          this.props.onUpload(fileContent, resourceId);
        })
        .catch(err => {
          currentFile.status = 'fail';
          this.setState({ fileList });
        });
    } else {
      Object.values(checkpoints).forEach(checkpoint => {
        const { uploadId, file } = checkpoint;
        const { type } = file;
        ossClient
          .multipartUpload(uploadId, file, {
            callback: {
              url: callbackData.url,
              body: callbackData.body,
              callbackSNI: true,
              contentType: callbackData.contentType
            },
            checkpoint,
            parallel: UPLOAD_PARALLEL,
            partSize: UPLOAD_PART_SIZE,
            progress: (progress, checkpoint) => {
              this.onMultipartUploadProgress(progress, checkpoint, currentFile);
            },
            mime: type
          })
          .then(url => {
            currentFile.status = 'success';
            this.setState({ fileList });
            this.props.onUpload(fileContent, resourceId);
          })
          .catch(err => {
            currentFile.status = 'fail';
            this.setState({ fileList });
          });
      });
    }
  }

  // 显示/隐藏上传进度
  handleToggleFileList = () => {
    this.setState({
      showFileList: !this.state.showFileList
    });
  }

  // 取消上传
  handleCancelAllUpload = () => {
    // 判断是否有正在上传或者待上传的文件，有的话弹出二次提示框
    const { fileList, ossClient, vendorType } = this.state;
    const uploadingFileList = fileList.filter(file => file.status === 'uploading' || file.status === 'waiting');
    if (uploadingFileList.length) {
      Modal.confirm({
        title: '放弃上传',
        content: '上传列表中有未上传完成的文件，确定要放弃上传吗？',
        icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
        onOk: () => {
          uploadingFileList.forEach((uploadingFile, index) => {
            if (vendorType === 'TENCENT_COS') {
              // 腾讯云取消上传
              if (uploadingFile.taskId) {
                ossClient.cancelTask(uploadingFile.taskId);
              }
            } else {
              // 阿里云取消上传
              const { checkpoints } = uploadingFile;
              Object.values(checkpoints).forEach(checkpoint => {
                const { uploadId, name } = checkpoint;
                ossClient.abortMultipartUpload(name, uploadId);
              });
            }
            fileList.splice(index, 1);
          });
          this.setState({ fileList: [] });
          this.props.onCancel();
        }
      })
      return;
    }
    this.setState({ fileList: [] });
    this.props.onCancel();
  }

  // 取消单个文件上传
  handleCancelUpload = (currentFile, index) => {
    const { fileList, ossClient, vendorType } = this.state;
    if (vendorType === 'TENCENT_COS') {
      // 腾讯云取消上传
      if (currentFile.taskId) {
        ossClient.cancelTask(currentFile.taskId);
      }
    } else {
      // 阿里云取消上传
      const { checkpoints } = currentFile;
      Object.values(checkpoints).forEach(checkpoint => {
        const { uploadId, name } = checkpoint;
        ossClient.abortMultipartUpload(name, uploadId);
      });
    }
    fileList.splice(index, 1);
    this.setState({ fileList });
    
    if (!fileList.length) {
      this.props.onCancel();
    }
  }

  renderOverallProgress = () => {
    // 至少有一个文件正在上传，显示正在上传 N 项
    // 文件全都上传成功，显示为“N 项上传成功”
    // 文件全都上传失败，显示为“N 项上传失败
    // 文件部分上传成功、部分上传失败，显示为“M 项上传成功，N 项上传失败”
    const { fileList } = this.state;
    const allFileLength = fileList.length;
    const successFileLength = fileList.filter(file => file.status === 'success').length;
    const failFileLength = fileList.filter(file => file.status === 'fail').length;
    const uploadingFileLength = fileList.filter(file => file.status === 'uploading' || file.status === 'waiting').length;

    if (!!uploadingFileLength) {
      return (
        <span>正在上传{uploadingFileLength}项</span>
      )
    }

    if (successFileLength === allFileLength ) {
      return (
        <span>{successFileLength}项上传完成</span>
      )
    }

    if (failFileLength === allFileLength) {
      return (
        <span>{successFileLength}项上传失败</span>
      )
    }

    return (
      <span>{successFileLength}项上传完成，{failFileLength}项上传失败</span>
    )
  }

  render()  {
    const { fileList, showFileList } = this.state;
    const { isOpen } = this.props;

    return (
      <div className={`prepare-lesson-upload-progress-modal ${isOpen ? 'visibile' : 'hidden'}`}>
        <div className={`modal-header ${showFileList ? 'no-radius' : ''}`}>
          {/* 总体进度 */}
          <div className="over-all-progress">
            { this.renderOverallProgress() }
          </div>

          <div className="operate">
            <Choose>
              <When condition={showFileList}>
                <span
                  className="icon iconfont"
                  onClick={this.handleToggleFileList}
                >&#xe835;</span>
              </When>
              <Otherwise>
                <span
                  className="icon iconfont"
                  style={{ transform: 'rotate(180deg)'}}
                  onClick={this.handleToggleFileList}
                >&#xe835;</span>
              </Otherwise>
            </Choose>
            <span
              className="icon iconfont"
              onClick={this.handleCancelAllUpload}
            >&#xe82f;</span>
          </div>
        </div>
        {
          showFileList &&
          <div className="modal-body" style={{height:`${fileList.length * 53}px`}}>
            {
              fileList.map((file, index) => {
                let { size, type, name } = file.fileContent;
                let _size = `${(size / DEFAULT_SIZE_UNIT).toFixed(1)}M`;
                if (size < 0.1 * DEFAULT_SIZE_UNIT) {
                  _size = `${(size / 1024).toFixed(1)}kb`;
                }

                if (!type) {
                  type = getFileTypeByName(name)
                }

                let imgSrc = URL.createObjectURL(file.fileContent);
                if (type.indexOf('image') < 0 && type !== 'folder') {
                  imgSrc =  FILE_TYPE_ICON_MAP[FILR_VERIFY_MAP[type].type]
                }

                return (
                  <div
                    className={`file-item ${file.status === 'fail' ? 'fail':''}`}
                    key={`file-item${index}`}
                    
                  >
                    <div className="file-item__cover">
                      <img src={imgSrc} alt="file-item-cover" />
                      <span className="file-name">{getEllipsText(name, 10)}</span>
                    </div>
                    <span className="file-item__size">{_size}</span>
                    <div className="file-item__status">
                      {
                        file.status === 'uploading' &&
                        <span>{file.progress}%</span>
                      }
                      {
                        file.status === 'success' &&
                        <span className="icon iconfont succ">&#xe710;</span>
                      }
                      {
                        file.status === 'fail' &&
                        [
                          <span
                            key="icon-fail"
                            className="icon iconfont fail"
                          >上传失败，请重试</span>,
                          <span
                            key="icon reupload"
                            className="icon iconfont reupload"
                            onClick={() => this.handleReUpload(index)}
                          >&#xe75a;</span>
                        ]
                      }
                      {
                        file.status === 'waiting' &&
                        <span className="icon iconfont waiting">排队中</span>
                      }
                      {
                        (file.status === 'uploading' || file.status === 'waiting') &&
                        <span
                          className="icon iconfont close"
                          onClick={() => this.handleCancelUpload(file, index)}
                        >&#xe6ef;</span>
                      }

                    </div>
                    {
                      file.status === 'uploading' &&
                      <div
                        className="file-item__progress"
                        style={{
                          width: `${file.progress}%`
                        }}
                      ></div>
                    }
                  </div>
                )
              })
            }
          </div>
        }
      </div>
    )
  }
}

export default UploadProgressModal;