
import React from 'react';
import { RendererProps } from '../../../../../factory';
import { FormItem, FormControlProps, FormBaseControl } from '../../../../Form/Item'
import Upload, { UploadProps } from 'antd/lib/Upload';
import Button from 'antd/lib/Button';

import { WrapperFileUpload } from './style'
import '../../../font/iconfont.css';
import MSG from '../../../utils/msgsub';
import SparkMD5 from 'spark-md5';
import { Binary } from '@babel/types';

interface FileParams {
  upload_cmd: 'check_exist' | 'chunk_upload' | 'chunk_merge' | {} & string;
  // 接口标识 check_exist : 文件存在校验 chunk_upload： 分片接口chunk_merge： 分片合并
  upload_name: string; // 文件名
  upload_field: string; // 文件对应字段
  file_md5?: string; // 文件md5
  chunk_index?: number; // 文件当前分片索引
  chunk_total?: number; // 文件分片总数
  chunk_size?: number; // 分片大小
  type?: string; // 文件类型
  chunk_file?: Binary; // 文件分片
  file_type: string;
}

interface FileUploadState {
  visible: boolean;
  showfilelist: any;
  params: string;
  turtyfilelist: any;
  loading: boolean;
  preUploading: boolean;  //预处理
  chunksSize: number; // 上传文件分块的总个数
  currentChunks: number; // 当前上传的队列个数 当前还剩下多少个分片没上传
  uploadPercent: number;  // 上传率
  preUploadPercent: number; // 预处理率
  uploadRequest: boolean; // 上传请求，即进行第一个过程中
  uploaded: boolean; // 表示文件是否上传成功
  uploading: boolean; // 上传中状态
  uploadParams: any;
  arrayBufferData: any;
  formItemName: string;
  current: any;
  fileSize: number;
  statusIconLists: any;
}

// {
//   [propName: string]: {
//     prevIcon: ReactNode;
//     nextIcon: ReactNode;
//   }
// }


export interface LionInputFileSchema extends FormBaseControl {
  type: 'lion-input-file';
}

export interface LionInputFileProps extends FormControlProps, Omit<LionInputFileSchema, 'type' | 'className' | 'descriptionClassName' | 'inputClassName'> { }

export class LionInputFile extends React.PureComponent<LionInputFileProps & UploadProps & RendererProps, FileUploadState & FileParams>{

  state = {
    visible: false,
    current: null as any,
    fileList: [],
    uploading: false,
    showfilelist: [] as any,
    params: '',
    turtyfilelist: [] as any,
    init: false,
    formItemName: '',
    removeIcon: true,
    baseUrl: '',
    loading: false,
    preUploading: false,   //预处理
    chunksSize: 0,   // 上传文件分块的总个数
    currentChunks: 0,  // 当前上传的队列个数 当前还剩下多少个分片没上传
    uploadPercent: -1,  // 上传率
    preUploadPercent: -1, // 预处理率
    uploadRequest: false, // 上传请求，即进行第一个过程中
    uploaded: false, // 表示文件是否上传成功
    uploadParams: {} as any,
    arrayBufferData: [] as any,
    upload_cmd: 'check_exist',
    upload_name: '',
    upload_field: '',
    file_type: '',
    fileSize: 0,
    statusIconLists: {
      "done": {
        prevIcon: <span className="fa fa-file-text-o"></span>,
        nextIcon: <span className="fa fa-close"></span>
      },
      "error": {
        prevIcon: <span style={{ color: "#ff4d4f" }} className="fa fa-file-text-o"></span>,
        nextIcon: <span className="fa fa-close"></span>
      },
      "success": {
        prevIcon: <span className="fa fa-file-text-o"></span>,
        nextIcon: <span className="fa fa-close"></span>
      },
      "uploading": {
        prevIcon: <span className="fa fa-circle-o-notch fa-spin"></span>,
        nextIcon: null
      },
      "removed": {
        prevIcon: <span className="fa fa-ban" ></span>,
        nextIcon: null
      },
    } as any
  };

  _merge: any = React.createRef()

  get fontSize() {
    return +getComputedStyle(window.document.documentElement)?.fontSize?.replace('px', '')
  }

  initFileData() {
    const { data, url, name, env, store } = this.props;
    const baseUrl = env?.axiosInstance?.defaults?.baseURL || env?.ajaxApi
    // onChange('');

    let content;
    content = data[name as string] || store?.data[name as string];
    this.setState({
      formItemName: 'temp_' + name
    })
    // this.props.onBulkChange({ ['temp_' + name as string]: '' });
    if (content) {
      const { info, value } = content;
      if (info && value && info.length !== 0) {
        const md5Map = value.split(',');
        let initialList: any = [];
        info.map((ele: any, idx: number) => {
          initialList.push({
            uid: md5Map[idx],
            status: 'done',
            name: ele.name || ele.fileName,
            url: (baseUrl ? baseUrl : '') + (ele?.addr || ele?.url),
            size: ele?.size
          })
        })
        this.setState({
          uploading: false,
          params: value,
          showfilelist: [
            ...initialList
          ],
          turtyfilelist: [
            ...initialList
          ]
        });

        // this.props.onBulkChange({ ['temp_' + name as string]: value });
      }
    }
  }

  componentDidUpdate(prevProps: any, prevState: any) {
    if (this.props !== prevProps) {
      this.initFileData()
    }
  }

  componentDidMount() {
    this.initFileData()
  }

  handleOpenPdf = (current: any) => {
    this.setState({
      visible: true,
      current: current
    });
  }

  openDownloadDialog = (url: any, saveName: string) => {
    return new Promise((resolve, reject) => {
      resolve('')
    }).then(res => {
      if (typeof url == 'object' && url instanceof Blob) {
        url = URL.createObjectURL(url); // 创建blob地址
      }
      var aLink = document.createElement('a');
      aLink.href = url;
      aLink.download = saveName || ''; // HTML5新增的属性，指定保存文件名，可以不要后缀，注意，file:///模式下不会生效
      var event;
      if (window.MouseEvent) event = new MouseEvent('click');
      else {
        event = document.createEvent('MouseEvents');
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      }
      aLink.dispatchEvent(event);
    })
  }

  handleRemove = (file: any) => {

    const { multiple, name } = this.props;
    const { params, uploading, showfilelist, turtyfilelist } = this.state;
    if (uploading) {
      MSG._error('正在上传中，无法执行删除操作');
      return false
    }


    let _item_val: any = {};
    if (multiple) {
      const index = showfilelist.indexOf(file);
      const isexist = turtyfilelist.findIndex((item: any, idx: any) => {
        return item.name === file.name && item.uid === file.uid && (item.addr || item.url) === (file.url || file.addr)
      })
      const newturtyfilelist = turtyfilelist.slice();
      const newFileList = showfilelist.slice();
      let newparams = params.slice();
      if (index !== -1 && isexist !== -1) {
        let example: any = params.split(',');
        newturtyfilelist.splice(isexist, 1);
        example.splice(isexist, 1);
        newparams = example.join(',');
      }
      newFileList.splice(index, 1);
      this.setState({
        showfilelist: newFileList,
        params: newparams,
        turtyfilelist: newturtyfilelist
      });
      _item_val = {
        value: newparams,
        info: newturtyfilelist.map((_file: any) => {
          return {
            name: _file.name,
            size: _file.size,
            addr: _file.addr ? _file.addr : _file.url,
          }
        })
      }
      this.props.onBulkChange({ [name as any]: _item_val });

    } else {
      _item_val = {
        value: '',
        info: []
      }
      this.setState({
        showfilelist: [],
        params: '',
        turtyfilelist: []
      });
      this.props.onBulkChange({ [name as any]: _item_val });
    }
    return true
  }



  matchLocalhost = (str: string) => {
    return /localhost\:(\d)+/.test(str)
  }

  handleMergeFile = () => {
    const { uploadParams } = this.state;
    this.handlePartUpload(uploadParams?.chunks, uploadParams?.file)
  }

  handlePartUpload = (uploadList: any, file: any) => {
    const { upload_cmd, upload_name, turtyfilelist, showfilelist, chunksSize, file_type, params, fileSize } = this.state;
    const { fileMd5 } = file;
    const { url, multiple, env, name } = this.props;
    const baseUrl = env?.axiosInstance?.defaults?.baseURL || env?.ajaxApi

    // 遍历uploadList
    try {
      // @ts-ignore
      Promise.allSettled(uploadList.map((_uploadItem: any) => {
        return new Promise((resolve: any, reject: any) => {
          const { chunkMd5, chunk, start, end } = _uploadItem;


          let formData = new FormData(),
            //新建一个Blob对象，将对应分片的arrayBuffer加入Blob中
            blob = new Blob([this.state.arrayBufferData[chunk - 1].currentBuffer], { type: 'application/octet-stream' });
          // 上传的参数
          let params = `fileMd5=${fileMd5}&chunkMd5=${chunkMd5}&chunk=${chunk}&start=${start}&end=${end}&chunks=${this.state.arrayBufferData.length}`

          // 通过formData将上传参数传入
          formData.append('upload_cmd', upload_cmd);
          formData.append('upload_field', String(name));
          formData.append('file_name', upload_name);
          formData.append('file_md5', fileMd5);
          formData.append('chunk_index', chunk);
          formData.append('chunk_total', String(chunksSize));
          formData.append('chunk_size', String(end - start));
          formData.append('type', file_type);
          formData.append('chunk_file', blob);

          env.fetcher({
            url: url,
            data: formData,
            method: 'post'
          }).then((res: any) => {
            if (res.status === 0) {
              resolve(res?.data)
            } else {
              reject({ _err: res?.msg, _data: formData })
            }
          })
        })
      })).then((_res: any) => {

        let _chunk_success_count: number = 0;
        let _chunk_error_arr: any = [];

        _res.map((_upload_res: any) => {
          if (_upload_res?.status === 'fulfilled') {
            _chunk_success_count++
          } else {
            _chunk_error_arr.push(_upload_res.reason._data)
          }
        })

        if (_chunk_success_count === chunksSize) {
          let formData = new FormData();
          // 通过formData将上传参数传入，发起文件分片合并通知
          formData.append('upload_cmd', 'chunk_merge');
          formData.append('upload_field', String(name));
          formData.append('file_name', upload_name);
          formData.append('file_md5', fileMd5);
          env.fetcher({
            url: url,
            data: formData,
            method: 'post'
          }).then((_merge_res: any) => {
            if (_merge_res?.status === 0) {
              const data: any = _merge_res?.data;
              let fileName: string = data?.fileName;
              let _url = data?.url;
              let value = data?.value;
              let str = params.slice();
              let _item_val: any;
              if (typeof str === 'string' && str.length === 0) {
                _item_val = {
                  value: value,
                  info: turtyfilelist.map((_file: any) => {
                    return {
                      name: _file.name,
                      size: _file.size,
                      addr: _file.addr ? _file.addr : _file.url,
                    }
                  }).concat([{ name: fileName, addr: _url, size: fileSize }])
                };
                this.setState({
                  params: value
                });
              } else {
                _item_val = {
                  value: str + ',' + value,
                  info: turtyfilelist.map((_file: any) => {
                    return {
                      name: _file.name,
                      size: _file.size,
                      addr: _file.addr ? _file.addr : _file.url,
                    }
                  }).concat([{ name: fileName, addr: _url, size: fileSize }])
                };
                this.setState({
                  params: str + ',' + value
                })
              }
              this.props.onBulkChange({ [String(name)]: _item_val });
              // this.props.onChange(_item_val)
              if (multiple) {
                this.setState({
                  uploading: false,
                  showfilelist: [
                    ...showfilelist,
                    { name: fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ],
                  turtyfilelist: [
                    ...turtyfilelist,
                    { name: fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ]
                });
              } else {
                this.setState({
                  uploading: false,
                  turtyfilelist: [
                    { name: fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ],
                  showfilelist: [
                    { name: fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ]
                });
              }
              MSG._success('文件上传成功');
            } else {
              if (multiple) {
                this.setState({
                  uploading: false,
                  showfilelist: [
                    ...showfilelist,
                    {
                      uid: this.uuid(),
                      status: 'error',
                      name: upload_name,
                    }
                  ]
                });
              } else {
                this.setState({
                  uploading: false,
                  showfilelist: [
                    {
                      uid: this.uuid(),
                      status: 'error',
                      name: upload_name,
                    }
                  ]
                });
              }
              MSG._error(_merge_res?.msg)
            }
          }).catch((_file_merge_res: any) => {
            if (multiple) {
              this.setState({
                uploading: false,
                showfilelist: [
                  ...showfilelist,
                  {
                    uid: this.uuid(),
                    status: 'error',
                    name: upload_name,
                  }
                ]
              });
            } else {
              this.setState({
                uploading: false,
                showfilelist: [
                  {
                    uid: this.uuid(),
                    status: 'error',
                    name: upload_name,
                  }
                ]
              });
            }
            MSG._error('上传失败');
            return;
          })
        } else {
          _chunk_error_arr.map((_chunk_error_item: any) => {
            env.fetcher({
              url: url,
              data: _chunk_error_item,
              method: 'post'
            }).then((_error_res: any) => {
            })
          })
          throw new Error('chunk file upload error!')
        }
      }).catch((_err: any) => {
        this.setState({
          uploading: false
        })
        MSG._error(_err);
        return;
      })
    } catch (error) {
      this.setState({
        uploading: false
      });
      MSG._error(error);
      return;
    }
  }

  uuid = () => {
    const temp_url = URL.createObjectURL(new Blob());
    const uuid = temp_url.toString();
    URL.revokeObjectURL(temp_url);
    return uuid.substr(uuid.lastIndexOf("/") + 1);
  }

  handleFile = (config: any) => {
    return new Promise((resolve: any, reject: any) => {
      let fileSize = config?.file?.size;
      let file = config?.file;
      if (fileSize > 20 * 1024 * 1024) {
        MSG._error('文件大小不能超过20M');
        return;
      }

      let blobSlice = File.prototype.slice; // 切片使用

      let _this = this;

      let chunkSize = 1024 * 1024 * 2;   // 切片每次2M
      let chunks = Math.ceil(file.size / chunkSize); // 切片数
      let currentChunk = 0; // 当前上传的chunk索引

      let file_type = config?.file?.type;
      let file_name = config?.file?.name;

      let spark = new SparkMD5.ArrayBuffer(); // 对arrayBuffer数据进行md5加密，产生一个md5字符串
      let chunkFileReader = new FileReader(); // 用于计算出每个chunkMd5
      let totalFileReader = new FileReader();  // 用于计算出总文件的fileMd5

      let params: any = { chunks: [], file: {} };  // 用于上传所有分片的md5信息

      let arrayBufferData: any = [];  // 用于存储每个chunk的arrayBuffer对象,用于分片上传使用

      params.file.fileName = file.name;
      params.file.fileSize = file.size;

      totalFileReader.readAsArrayBuffer(file);

      totalFileReader.onload = function (e: any) {
        // 对整个totalFile生成md5
        spark.append(e.target.result)
        params.file.fileMd5 = spark.end() // 计算整个文件的fileMd5
      }

      chunkFileReader.onload = function (e: any) {
        // 对每一片分片进行md5加密
        spark.append(e.target.result)
        // 每一个分片需要包含的信息
        let obj = {
          chunk: currentChunk + 1,
          start: currentChunk * chunkSize, // 计算分片的起始位置
          end: ((currentChunk * chunkSize + chunkSize) >= file.size) ? file.size : currentChunk * chunkSize + chunkSize, // 计算分片的结束位置
          chunkMd5: spark.end(),
          chunks
        }
        // 每一次分片onload,currentChunk都需要增加，以便来计算分片的次数
        currentChunk++;
        params.chunks.push(obj)

        // 将每一块分片的arrayBuffer存储起来，用来partUpload
        let tmp = {
          chunk: obj.chunk,
          currentBuffer: e.target.result
        }
        arrayBufferData.push(tmp)

        if (currentChunk < chunks) {
          // 当前切片总数没有达到总数时
          loadNext()

          // 计算预处理进度
          _this.setState({
            preUploading: true,
            preUploadPercent: Number((currentChunk / chunks * 100).toFixed(2))
          })
        } else {
          //记录所有chunks的长度
          params.file.fileChunks = params.chunks.length
          // 表示预处理结束，将上传的参数，arrayBuffer的数据存储起来
          _this.setState({
            preUploading: false,
            uploadParams: params,
            arrayBufferData,
            chunksSize: chunks,
            preUploadPercent: 100,
            file_type: file_type,
            upload_name: file_name,
            fileSize: fileSize
          }, () => {
            resolve()
          })
        }
      }

      chunkFileReader.onerror = function () {
        console.warn('oops, something went wrong.');
      };

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

      loadNext();
    })
  }



  handleUpload = (config: any) => {
    const { url, env, name, multiple, limitSize, limitType } = this.props;
    const { showfilelist, turtyfilelist, params } = this.state;
    const baseUrl = env?.axiosInstance?.defaults?.baseURL || env?.ajaxApi
    let fileSize = config?.file?.size;
    if (limitSize && (fileSize > limitSize)) {
      MSG._error(`文件大小不能超过${limitSize}KB`);
      return;
    }
    if (limitType && !(limitType.split(',').some((_: any) => {
      return config?.file?.type?.endsWith(_)
    }))) {
      MSG._error(`请上传 ${limitType} 类型的文件!`);
      return;
    }
    const formData = new FormData();
    this.setState({
      uploading: true,
      fileSize: 0
    });
    this.handleFile(config).then(() => {
      const { uploadParams } = this.state;
      formData.append('upload_cmd', 'check_exist');
      formData.append('upload_field', String(name));
      formData.append('file_name', uploadParams?.file.fileName);
      formData.append('file_md5', uploadParams?.file.fileMd5);
      env.fetcher({
        url: url,
        data: formData,
        method: 'post',
        dataType: 'form-data'
      }).then((res: any) => {
        if (res?.status === 0 && res?.data) {
          const { exist } = res?.data;
          switch (exist) {
            case true:
              MSG._info('文件上传成功');
              let _url = res?.data?.url;
              let value = res?.data?.value;
              let str = typeof params === 'string' && params.length === 0 ? value : (params + ',' + value);
              let _item_val: any = {
                value: str,
                info: turtyfilelist.map((_file: any) => {
                  return {
                    name: _file.name,
                    size: _file.size,
                    url: _file.addr ? _file.addr : _file.url,
                  }
                }).concat([{ name: uploadParams?.file.fileName, url: _url, size: fileSize }])
              };
              if (multiple) {
                this.setState({
                  uploading: false,
                  showfilelist: [
                    ...showfilelist,
                    { name: uploadParams?.file.fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ],
                  turtyfilelist: [
                    ...turtyfilelist,
                    { name: uploadParams?.file.fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ]
                });
              } else {
                this.setState({
                  uploading: false,
                  turtyfilelist: [
                    { name: uploadParams?.file.fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ],
                  showfilelist: [
                    { name: uploadParams?.file.fileName, addr: (baseUrl ? baseUrl : '') + _url, uid: value, status: 'done', size: fileSize }
                  ]
                });
              };
              this.props.onBulkChange({ [name as any]: _item_val })
              this.setState({
                uploadParams: { chunks: [], file: {} },
                arrayBufferData: [],
                chunksSize: 0,
                preUploading: false,
                file_type: '',
                upload_name: '',
                uploading: false,
                params: str
              })
              break;
            case false:
              this.setState({
                upload_cmd: 'chunk_upload'
              }, () => {
                this.handleMergeFile()
                // 上传分片文件
              });
              break;
            default:
              break;
          }
        } else {
          this.setState({
            uploading: false
          })
          MSG._error('文件上传失败，系统异常');
          return;
        }
      })
    })
    return;
  }

  render() {
    const { url, multiple, env, limitType, classnames: cx, charWidth } = this.props;
    const { uploading, showfilelist } = this.state;
    const baseUrl = env?.axiosInstance?.defaults?.baseURL || env?.ajaxApi;
    const accept = limitType && limitType?.length > 0 ? limitType?.split(',').map((_itemType: string) => {
      return ('.' + _itemType)
    }).join(',') : '*';

    const uploadButton = (
      <div style={{
        display: 'inline-block',
        textAlign: 'center',
        cursor: 'pointer',
        outline: 'none',
        marginTop: '12px',
        fontSize: '12px',
        color: '#444'
      }}>
        <Button
          size='middle'
          style={{
            width: '56px',
            height: '32px',
            background: '#fff',
            border: '1px solid #3168f0',
            borderRadius: '4px',
            color: '#3168f0',
            fontWeight: 500,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            fontSize: '14px !important',
            padding: '9px 15px'
          }}
          disabled={
            this.props.disabled ? this.props.disabled :
              // 判断是否多选
              multiple ? (uploading ? true : false) : (uploading ? true : (showfilelist.length > 0 ? true : false))
          }
        >
          {uploading ? <span className="fa fa-circle-o-notch fa-spin"></span> : '上传'}
        </Button>
      </div>
    );

    const uploadProps: UploadProps = {
      // action: url,
      listType: "text",
      // disabled: multiple ? false : (showfilelist.length >= 1 ? true : false),
      iconRender: () => <span className="fa fa-file-text-o"></span>,
      showUploadList: {
        showPreviewIcon: true,
        showRemoveIcon: true,
        showDownloadIcon: true,
        downloadIcon: (file: any) => <span style={{ fontSize: '12px' }} className="fa fa-cloud-download"></span>,
        removeIcon: (file: any) => <span style={{ fontSize: '12px' }} className="fa fa-close"></span>
      },
      onRemove: this.handleRemove,
      customRequest: this.handleUpload,
      fileList: showfilelist,
      onPreview: (file: any) => false,
      onDownload: (file: any) => {
        const downloadFileUlr = file?.url || file?.addr;
        this.openDownloadDialog(downloadFileUlr, file?.name)
      },
      accept: accept
    };

    return (
      <WrapperFileUpload className={cx(`Form-control`, charWidth ? 'Form-control--width' : '')}>
        <Upload style={{ width: '70px', fontSize: '12px' }} {...uploadProps}>
          {uploadButton}
        </Upload>
        <button style={{ display: 'none', pointerEvents: 'none' }}></button>
      </WrapperFileUpload>
    );
  }
}

@FormItem({
  type: 'lion-input-file',
  strictMode: false
})
export class LionInputFileRenderer extends LionInputFile { }
