import { Button, Modal, Upload, message } from "antd";
import type { UploadRequestOption as RcCustomRequestOptions } from "rc-upload/lib/interface";
import {
  CloudUploadOutlined,
  PlusOutlined,
  UploadOutlined,
} from "@ant-design/icons";
import { FC, useContext, useMemo, useState } from "react";
import { RcFile, UploadFile, UploadProps } from "antd/es/upload";
import {
  DageFileAPIResponseType,
  DageUploadProps,
  DageUploadType,
} from "./types";
import {
  AntdDragger,
  AntdDraggerText,
  AntdUpload,
  UploadContainer,
  UploadTips,
} from "./style";
import { DageUploadContext } from "./context";
import { service } from "../../utils";
import { DageUploadItemActions } from "./ItemActions";
import { getImageSize } from "./utils";

const getBase64 = (file: RcFile): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = (error) => reject(error);
  });

export const DageUpload: FC<DageUploadProps> = ({
  action,
  value,
  dType = DageUploadType.IMG,
  maxCount = 9,
  maxSize = 5,
  tips,
  disabled,
  name = "file",
  onChange,
  ...rest
}) => {
  const context = useContext(DageUploadContext);
  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewImage, setPreviewImage] = useState("");
  const [previewTitle, setPreviewTitle] = useState("");
  const uploadListType = useMemo(() => {
    switch (dType) {
      case DageUploadType.IMG:
        return "picture-card";
      default:
        return "text";
    }
  }, [dType]);
  const isPictureCard = uploadListType === "picture-card";

  // 可选文件类型
  const accept = useMemo(() => {
    switch (dType) {
      case DageUploadType.IMG:
        return ".jpg,.jpeg,.png,.gif";
      case DageUploadType.MOBILE_MODEL:
        return ".zip";
      case DageUploadType.MODEL:
        return ".4dage";
      case DageUploadType.VIDEO:
        return ".mp4";
      case DageUploadType.AUDIO:
        return ".mp3";
      default:
        return "*";
    }
  }, [dType]);

  const beforeUpload = (file: RcFile) => {
    let pass = false;
    let passFileType = false;

    // 校验文件类型
    const fileName = file.name.toLowerCase();
    const fileExtension = fileName.substring(fileName.lastIndexOf("."));
    passFileType = accept.split(",").includes(fileExtension);
    if (!passFileType) {
      message.error(tips || "选择的文件类型不正确！");
    }

    // 校验文件大小
    const isLtM = file.size / 1024 / 1024 < maxSize;
    if (!isLtM) {
      message.error(`最大支持 ${maxSize}M!`);
    }

    pass = passFileType && isLtM;

    pass && context?.handleUploadingFileNum("add");

    return pass ? pass : Upload.LIST_IGNORE;
  };

  const onUpload = (option: RcCustomRequestOptions) => {
    const formData = new FormData();
    const headers = option.headers || {};

    formData.append("type", dType);

    if (option.file instanceof Blob) {
      formData.append(name, option.file, (option.file as any).name);
    } else {
      formData.append(name, option.file);
    }

    option.data &&
      Object.keys(option.data).forEach((key) => {
        const value = option.data?.[key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
          return;
        }

        formData.append(key, value as string | Blob);
      });

    service
      .post<DageFileAPIResponseType>(action, formData, {
        withCredentials: option.withCredentials,
        // @ts-ignore
        headers: {
          Accept: "*/*",
          "Content-Type": "multipart/form-data",
          ...headers,
        },
        onUploadProgress: ({ total, loaded }) => {
          if (!total || !loaded) return;
          option.onProgress?.({ percent: Math.round((loaded / total) * 100) });
        },
      })
      .then(({ data }) => {
        option.onSuccess?.(data);
      })
      .catch((err) => {
        context?.handleUploadingFileNum("uploaded");
        option.onError?.(err);
      });
  };

  const handleChange: UploadProps["onChange"] = async ({
    fileList: newFileList,
    file,
  }) => {
    if (file.status === "done") {
      if (typeof file.response !== "object" || !file.response.id) {
        // 上传失败
        file.status = "error";
      } else if (isPictureCard && file.originFileObj) {
        // 如果是图片获取图片宽高
        // @ts-ignore
        file.imgAttrs = await getImageSize(file.originFileObj);
      }

      context?.handleUploadingFileNum("uploaded");
    }
    onChange?.(
      newFileList.map((i) => ({
        ...i,
        dType,
      })),
      {
        ...file,
        dType,
      }
    );
  };

  const handleCancel = () => setPreviewOpen(false);

  const handlePreview = async (file: UploadFile) => {
    if (!isPictureCard) return;

    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj as RcFile);
    }

    setPreviewImage(file.url || (file.preview as string));
    setPreviewOpen(true);
    setPreviewTitle(
      file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1)
    );
  };

  const handleDownload = async (file: UploadFile) => {
    if (!file.url) return;

    const res: BlobPart = await service.get(file.url, {
      responseType: "arraybuffer",
    });
    const blob = new Blob([res]);
    const url = URL.createObjectURL(blob);

    const link = document.createElement("a");
    link.href = url;
    link.target = "_blank";
    link.download = file.name;
    link.style.display = "none";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  const props: UploadProps = {
    fileList: value,
    withCredentials: true,
    customRequest: onUpload,
    listType: uploadListType,
    maxCount,
    accept,
    disabled,
    itemRender: (node, file, fileList, actions) => (
      <DageUploadItemActions
        isPictureCard={isPictureCard}
        disabled={disabled}
        file={file}
        actions={actions}
      >
        {node}
      </DageUploadItemActions>
    ),
    showUploadList: {
      showPreviewIcon: false,
      showDownloadIcon: false,
      showRemoveIcon: false,
    },
    multiple: maxCount > 1,
    onPreview: handlePreview,
    beforeUpload: beforeUpload,
    onChange: handleChange,
    onDownload: handleDownload,
    ...rest,
  };

  return (
    <UploadContainer>
      {[DageUploadType.MODEL, DageUploadType.MOBILE_MODEL].includes(dType) ? (
        <AntdDragger style={{ padding: "0 24px", width: "320px" }} {...props}>
          <CloudUploadOutlined style={{ fontSize: "40px", color: "#1677ff" }} />

          <AntdDraggerText>
            将文件拖到此处，或<span>点击上传</span>
          </AntdDraggerText>
          <UploadTips className="dage-upload__tips">{tips}</UploadTips>
        </AntdDragger>
      ) : (
        <>
          <AntdUpload {...props}>
            {isPictureCard ? (
              !disabled &&
              (!value || value.length < maxCount) && <PlusOutlined />
            ) : (
              <Button disabled={disabled} icon={<UploadOutlined />}>
                上传
              </Button>
            )}
          </AntdUpload>

          {!!tips && (
            <UploadTips
              style={{ marginTop: isPictureCard ? 0 : "8px" }}
              className="dage-upload__tips"
            >
              {tips}
            </UploadTips>
          )}
        </>
      )}

      <Modal
        open={previewOpen}
        title={previewTitle}
        footer={null}
        onCancel={handleCancel}
      >
        <img alt="example" style={{ width: "100%" }} src={previewImage} />
      </Modal>
    </UploadContainer>
  );
};

export * from "./types";
export * from "./context";
