import { useState, useRef } from 'react';
import { message } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import Request from '@ywfe/request';
import { RcFile } from 'antd/lib/upload/interface';
import {
  usePostNewResourceUploadfiles,
  useGetOssGetsignatureinfo,
  useGetOssGetgeneratepresignedurl,
} from '../../generated/api';
import {
  getFileType,
  getXmlNode,
  transformFileName,
  checkMultiple,
  checkSize,
  checkAccept,
  checkWidthHeight,
} from '../common/utils';
import { YUploadFile } from '../common/interface';
import { ChangeType } from '../upload.interface';
import {
  UploadButtonProps,
  OSSDataParams,
  OriginalFileProps,
  WaitFileListProps,
} from './interface';

export const useModel = (props: UploadButtonProps) => {
  let {
    // oss
    isOss,
    projectId,
    featureName,
    ossDir,
    securityType,
    expire,
    // 裁剪
    ableCrop,
    //
    uploadType,
    setFileList,
    fileListRef,
    // 文件限制相关配置
    multiple,
    accept,
    isAccept = true,
    maxSize,
    picWidth,
    picHeight,
    beforeUpload: customBeforeUpload,
    // request上传配置
    fileName: fileNameProp,
    name,
    requestKeyName,
    request: requestMethod,
    // 方法相关配置
    uploadFinish,
    //
    cropperModalRef,
  } = props;

  const isRequestOss = isOss && projectId && (featureName || ossDir);
  accept =
    uploadType !== 'uploadPicture' && !accept ? '.pdf, .xlsx, .xls' : accept;
  accept =
    uploadType === 'uploadPicture' && !accept
      ? 'image/jpg, image/JPG, image/jpeg, image/JPEG, image/png, image/PNG, image/webp, image/WEBP'
      : accept;
  accept = !isAccept ? '' : accept;

  const { postNewResourceUploadfilesData } = usePostNewResourceUploadfiles();
  const { getOssGetsignatureinfoData } = useGetOssGetsignatureinfo();
  const {
    getOssGetgeneratepresignedurlData,
  } = useGetOssGetgeneratepresignedurl();

  const ossWaitFileListRef = useRef<{ isLoading: boolean; list: RcFile[] }>({
    list: [],
    isLoading: false,
  });
  const waitFileListRef = useRef<WaitFileListProps>({
    status: 0,
    list: [],
  });
  const uploadResultRef = useRef<{ success: YUploadFile[]; fail: RcFile[] }>({
    success: [],
    fail: [],
  });
  const OSSDataRef = useRef<OSSDataParams>({
    host: '',
    accessId: '',
    policy: '',
    signature: '',
    dir: '',
  });
  const [, setUploadingStatus] = useState(0);
  const [, setGetOssConfig] = useState(false);

  // oss
  // 获取oss上传配置
  const getOSSDataAPI = async () => {
    const date = new Date();
    const dateFile = `${date.getFullYear()}/${
      date.getMonth() + 1
    }/${date.getDate()}/`;
    const typeFile = uploadType === 'uploadPicture' ? 'image-cdn' : 'file-cdn';
    // 确保ossDir地址, 开头无/, 结尾有/
    if (ossDir) {
      ossDir = ossDir.charAt(ossDir.length - 1) === '/' ? ossDir : ossDir + '/';
      ossDir =
        ossDir.charAt(0) === '/' ? ossDir.substr(1, ossDir.length - 1) : ossDir;
    }
    try {
      const res = getOssGetsignatureinfoData({
        projectId: projectId || '',
        dir: ossDir || `${projectId}/${featureName}/${typeFile}/${dateFile}`,
        securityType,
      });
      return res;
    } catch (_error) {}
  };
  // 获取配置后重新请求
  const OSSCustomRequest = async (file: RcFile) => {
    ossWaitFileListRef.current.list = [
      ...ossWaitFileListRef.current.list,
      file,
    ];
    if (ossWaitFileListRef.current.isLoading) {
      // 加入等待列表
      return;
    }
    ossWaitFileListRef.current.isLoading = true;
    setGetOssConfig(true);
    try {
      const res: any = await getOSSDataAPI();
      if (!res) {
        getOSSConfigFail();
        return;
      }
      OSSDataRef.current = res;
      ossWaitFileListRef.current.list.forEach((item) => customRequest(item));
      ossWaitFileListRef.current = {
        list: [],
        isLoading: false,
      };
    } catch (_error) {
      getOSSConfigFail();
    }
  };
  // oss请求失败
  const getOSSConfigFail = () => {
    uploadResultRef.current.fail = [
      ...uploadResultRef.current.fail,
      ...ossWaitFileListRef.current.list,
    ];
    ossWaitFileListRef.current = {
      list: [],
      isLoading: false,
    };
    setGetOssConfig(false);
    // 最后一次
    lastFailRequest();
  };
  // 获取临时链接
  const getPreviewUrl = async (ossFile: string) => {
    const params: { [name: string]: any } = {
      projectId: projectId || '',
      ossFile,
    };
    if (expire) params.expire = expire;
    try {
      const res: any = await getOssGetgeneratepresignedurlData(params as any);
      return res && res.url;
    } catch (_error) {
      return null;
    }
  };
  // 裁剪
  const cutImgPrivate = () => {
    const srcList: string[] = [];
    const fileList: RcFile[] = [];
    waitFileListRef.current.list.forEach((item) => {
      const reader = new FileReader();
      reader.readAsDataURL(item);
      reader.onload = (event: ProgressEvent<FileReader>) => {
        srcList.push(
          (event && event.target && (event.target.result as string)) || '',
        );
        fileList.push(item);
        if (srcList.length === waitFileListRef.current.list.length) {
          cropperModalRef.current?.setCropSrc(srcList);
          cropperModalRef.current?.setUploadFileList(fileList);
          cropperModalRef.current?.setCropSrcModalShow(1);
        }
      };
    });
  };
  // 上传到oss
  const uploadToOssAPI = async (
    host: string,
    file: RcFile,
    formData: FormData,
    fileName: string,
  ) => {
    const { dir, policy, accessId, signature } = OSSDataRef.current;
    formData.append('key', transformFileName(file, dir));
    formData.append('policy', policy);
    formData.append('OSSAccessKeyId', accessId);
    formData.append('success_action_status', '201');
    formData.append('signature', signature);
    formData.append('file', file, fileName);

    try {
      const res: any = await Request.post(host, formData);
      const ossFile = res && getXmlNode(res, 'Key');
      let url = res && getXmlNode(res, 'Location');

      if (ossFile && securityType === 'private') {
        url = await getPreviewUrl(ossFile);
      }

      return (
        url && {
          url: url,
          fileType: getFileType(url),
          ossFile,
        }
      );
    } catch (_error) {}
  };

  // 上传到接口
  const baseRequestAPI = async (formData: any): Promise<any> => {
    if (fileNameProp) formData.append('fileNames', fileNameProp);
    const res: any = await postNewResourceUploadfilesData(formData, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    });
    return (
      res &&
      res.list &&
      res.list[0] && {
        url: res.list[0].uploadAddress,
        fileName: res.list[0].uploadFileName,
      }
    );
  };
  // 上传状态判断
  const reduceUploadStatusPrivate = (isAdd?: boolean) => {
    const { status } = waitFileListRef.current;
    waitFileListRef.current.status = isAdd ? status + 1 : status - 1;
    setUploadingStatus(waitFileListRef.current.status);
  };
  // 检测
  const checkFile = async (file: RcFile, uploadFileList: RcFile[]) => {
    const fileNumLimit =
      multiple && (await checkMultiple(file, uploadFileList, props));
    const sizeLimit = maxSize && (await checkSize(file, props));
    const acceptLimit = accept && (await checkAccept(file, accept));
    const widthHeightLimit =
      getFileType(file.name) === 'image' &&
      !ableCrop &&
      picWidth &&
      picHeight &&
      (await checkWidthHeight(file, props));
    const customLimit =
      customBeforeUpload && (await customBeforeUpload(file, uploadFileList));

    return (
      fileNumLimit &&
      sizeLimit &&
      acceptLimit &&
      widthHeightLimit &&
      customLimit
    );
  };
  // 上传前校验
  const beforeUpload: any = async (file: RcFile, uploadFileList: RcFile[]) => {
    return new Promise(async (_resolve, reject) => {
      if (
        !ossWaitFileListRef.current.isLoading &&
        waitFileListRef.current.status === 0
      ) {
        const fileIndex = uploadFileList.findIndex(
          (item) => item.uid === file.uid,
        );
        if (fileIndex === 0) {
          // 开始新一轮的上传
          uploadResultRef.current = { success: [], fail: [] };
          waitFileListRef.current = { list: [], status: 0 };
          // 校验获取待上传文件列表
          for (let index = 0; index < uploadFileList.length; index++) {
            try {
              await checkFile(uploadFileList[index], uploadFileList);
              waitFileListRef.current.list = [
                ...waitFileListRef.current.list,
                uploadFileList[index],
              ];
            } catch (_error) {}
          }

          // 裁剪
          if (uploadType === 'uploadPicture' && ableCrop) {
            cutImgPrivate();
          } else {
            waitFileListRef.current.list.forEach((item) => customRequest(item));
          }
        }
      }
      reject();
    }).then(
      () => {
        return file;
      },
      () => {
        return Promise.reject('终止upload上传');
      },
    );
  };
  // 上传中
  const customRequest = async (file: RcFile) => {
    const formData = new FormData();
    const fileName = file.name || `${uuidv4()}.png`;
    const nowTime = new Date().valueOf() + 3 * 1000;

    if (
      isRequestOss &&
      !(
        OSSDataRef.current.expire &&
        Number(OSSDataRef.current.expire) * 1000 >= nowTime
      )
    ) {
      await OSSCustomRequest(file);
      return;
    }

    await reduceUploadStatusPrivate(true);

    // 是否走oss
    if (isRequestOss) {
      try {
        uploadToOssAPI(OSSDataRef.current.host, file, formData, fileName).then(
          (res: any) => {
            //TODO
            if (!res) {
              uploadResult(false, file);
              return;
            }
            const { url, ...other } = res;
            const lastName = url.split('/').pop() || '';
            onUploadDonePrivate(url, lastName, {
              ...other,
              originalName: fileName,
            });
            if (uploadFinish) uploadFinish(res);
          },
        );
      } catch (error) {
        uploadResult(false, file);
      }
    } else {
      formData.append(requestKeyName || name, file, fileName);

      const apiRequestMethod = requestMethod || baseRequestAPI;
      try {
        const res: any = await apiRequestMethod(formData);
        //TODO
        if (!res) {
          uploadResult(false, file);
          return;
        }

        if (typeof res === 'string') {
          onUploadDonePrivate(res, fileName, {
            originalName: fileName,
            fileType: getFileType(res),
          });
        } else {
          let imgObj = res;
          if (res.list && res.list[0]) imgObj = res.list[0];

          const {
            name,
            fileName,
            uploadFileName,
            url,
            fileUrl,
            uploadAddress,
          } = imgObj;

          const lastFileName = fileName || name || uploadFileName || fileName;
          const imgUrl = fileUrl || url || uploadAddress;
          if (imgUrl) {
            onUploadDonePrivate(imgUrl, lastFileName, {
              originalName: fileName,
              fileType: getFileType(imgUrl),
            });
          }
        }
        if (uploadFinish) uploadFinish(res);
      } catch (e) {
        uploadResult(false, file);
      }
    }
  };
  // 裁剪结束
  const cutResult = () => {
    if (!waitFileListRef.current.status) {
      setFileList(
        fileListRef.current,
        uploadResultRef.current.success,
        ChangeType.UPLOAD,
      );
    }
  };
  // 最后一次失败上传
  const lastFailRequest = () => {
    const successLength = uploadResultRef.current.success.length;
    const failLength = uploadResultRef.current.fail.length;
    if (
      successLength > 0 &&
      failLength + successLength === waitFileListRef.current.list.length
    ) {
      cutResult();
    }
  };
  // 上传结果
  const uploadResult = (isSuccess?: boolean, file?: RcFile) => {
    reduceUploadStatusPrivate();
    if (isSuccess) {
      message.success('上传成功！');
    } else if (!isSuccess && file) {
      uploadResultRef.current.fail = [...uploadResultRef.current.fail, file];
      lastFailRequest();
    }
  };
  // 上传完成
  const onUploadDonePrivate = (
    imgUrl: string,
    apiFileName: string = '',
    originalprops: OriginalFileProps,
  ) => {
    uploadResult(!!imgUrl);
    if (!imgUrl) return;

    let tempList = [...fileListRef.current];
    const fileItem: YUploadFile = {
      ...originalprops,
      uid: uuidv4(),
      size: 100,
      name: apiFileName || imgUrl.split('/').pop() || '',
      status: 'done',
      url: imgUrl,
      type: '',
    };
    tempList.push({ ...fileItem });
    fileListRef.current = tempList;
    uploadResultRef.current.success = [
      ...uploadResultRef.current.success,
      fileItem,
    ];

    const { list, status } = waitFileListRef.current;
    const { success, fail } = uploadResultRef.current;

    setFileList(
      fileListRef.current,
      success,
      !status &&
        cropperModalRef.current?.cropSrcModalShow === 0 &&
        fail.length + success.length === list.length
        ? ChangeType.UPLOAD
        : undefined,
    );
  };

  return {
    cutResult,
    beforeUpload,
    customRequest,
    data: {
      accept,
      uploadingStatus: waitFileListRef.current.status,
      isOssLoading: ossWaitFileListRef.current.isLoading,
    },
  };
};
