import { AxiosResponse } from "axios";
import { objTypeOf, blobToText, objAssign } from "./tool";

const parseDownloadFilename = (dispositionInfo: string): string => {
  if (!dispositionInfo) return "";
  const matchs = dispositionInfo.match(/filename=(.*)/) || [];
  const filename = matchs[1];
  return filename ? decodeURI(filename) : "";
};

/**
 * 下载url
 * @param {String} url 文件下载地址
 * @param {String}} filename 文件名
 */
export function downloadByUrl(url: string, filename?: string): void {
  const tempLink = document.createElement("a");
  tempLink.style.display = "none";
  tempLink.href = url;
  const fileName = filename || url.split("/").pop()?.split("?")[0];
  tempLink.setAttribute("download", fileName || "");
  if (tempLink.download === "undefined") {
    tempLink.setAttribute("target", "_blank");
  }
  document.body.appendChild(tempLink);
  tempLink.click();
  document.body.removeChild(tempLink);
  window.URL.revokeObjectURL(url);
}

/**
 * 下载blob
 * @param {Blob} data blob对象
 * @param {String} filename 文件名
 */
export function downloadByBlob(data: Blob, filename: string): void {
  const blobUrl = URL.createObjectURL(data);
  downloadByUrl(blobUrl, filename);
}

type ApiFunc = () => Promise<AxiosResponse<Blob>>;

/**
 * 通过api下载文件
 * @param {AxiosPromise} fetchFunc 下载接口
 * @param {String}} fileName 文件名
 * @returns Promise<Boolean>
 */
export async function downloadByApi<T extends ApiFunc>(
  axiosFetchFunc: T,
  fileName?: string
) {
  let errorInfo = null;
  const res = await axiosFetchFunc().catch((e) => {
    errorInfo = e;
  });

  // 展示后台错误信息
  if (errorInfo && objTypeOf(errorInfo) === "blob") {
    const textRes = await blobToText(errorInfo as Blob).catch(() => false);
    if (!textRes) return Promise.reject(new Error("下载失败！"));
    const resJson: { message: string } = JSON.parse(textRes as string);
    return Promise.reject(resJson.message);
  }

  const result = res as AxiosResponse<Blob>;
  const filename =
    fileName || parseDownloadFilename(result.headers["content-disposition"]);
  downloadByBlob(new Blob([result.data]), filename);
  return true;
}

/**
 * 下载图片
 * @param {String} url 图片地址
 * @param {String} filename 下载的文件名
 * @returns Promise<Boolean>
 */
export function downloadByImgUrl(
  url: string,
  filename: string
): Promise<boolean> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    // 跨域支持，需要服务端辅助配置
    // img.crossOrigin = "";
    img.onload = () => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      if (!canvas || !ctx) {
        reject(new Error("不支持下载!"));
        return;
      }

      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
      canvas.toBlob((blob) => {
        if (blob) {
          downloadByBlob(blob, filename);
          resolve(true);
          return;
        }
        reject(new Error("blob empty"));
      });
    };
    img.onerror = (e) => {
      reject(e);
    };
    img.src = url;
  });
}

interface XhrDownloadOption {
  type: "get" | "post";
  url: string;
  data: Record<string, any>;
  fileName: string;
  header: Record<string, any>;
  responseType: XMLHttpRequestResponseType;
}

/**
 * 文件流下载
 * @param {Object} option 文件下载设置
 */
export function xhrDownload(option: Partial<XhrDownloadOption>) {
  const defOpt = {
    type: "get",
    url: "",
    data: {} as Record<string, any>,
    fileName: "",
    header: {} as Record<string, any>,
    responseType: "" as XMLHttpRequestResponseType,
  };
  const opt = objAssign(defOpt, option);

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(opt.type.toUpperCase(), opt.url, true);
    if (opt.responseType) xhr.responseType = opt.responseType;

    // header set
    if (opt.header && objTypeOf(opt.header) === "object") {
      Object.keys(opt.header).forEach((key) => {
        xhr.setRequestHeader(key, opt.header[key]);
      });
    }

    function xhrOnload() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject(new Error("请求错误！"));
      }
    }
    xhr.onload = xhrOnload;

    if (opt.type.toUpperCase() === "POST") {
      const fromData = new FormData();
      Object.keys(opt.data).forEach((key) => {
        fromData.append(key, opt.data[key]);
      });
      xhr.send(fromData);
    } else {
      xhr.send();
    }
  });
}
