export interface IgnoreOption {
  el: string;
  height?: string;
  width?: string;
  scale?: number;
  fontSize?: number;
}
export interface AutofitOption {
  el?: string;
  dw?: number;
  dh?: number;
  resize?: boolean;
  ignore?: (IgnoreOption | string)[];
  transition?: number;
  delay?: number;
  limit?: number;
  cssMode?: "scale" | "zoom";
  allowScroll?: boolean;
}

declare interface Autofit {
  /**
   * 参数列表
   * 对象：
   *
   * @param {AutofitOption|String|undefined} options
   * @param {boolean|undefined} isShowInitTip
   * - 传入对象，对象中的属性如下：
   * - el（可选）：渲染的元素，默认是 "body"
   * - dw（可选）：设计稿的宽度，默认是 1920
   * - dh（可选）：设计稿的高度，默认是 1080
   * - resize（可选）：是否监听resize事件，默认是 true
   * - ignore(可选)：忽略缩放的元素（该元素将反向缩放），参数见readme.md
   * - transition（可选）：过渡时间，默认是 0
   * - delay（可选）：延迟，默认是 0
   * - limit（可选）：缩放限制，默认是 0.1
   * - cssMode（可选）：缩放模式，默认是 scale，可选值有 scale 和 zoom, zoom 模式可能对事件偏移有利
   */
  init(options?: AutofitOption | string, isShowInitTip?: boolean): void;
  /**
   * @param {String} id
   * 关闭autofit.js造成的影响
   *
   */
  off(id?: string): void;
  /**
   * 检查autofit.js是否正在运行
   */
  isAutofitRunning: boolean;
  /**
   * @param {string} el - 待处理的元素选择器
   * @param {boolean} isKeepRatio - 是否保持纵横比（可选，默认为true，false时将充满父元素）
   * @param {number|undefined} level - 缩放等级，用于手动调整缩放程度(可选，默认为 1)
   */
  elRectification: typeof elRectification;

  /**
   * 当前缩放比例
   */
  scale: number
}

// type Ignore = Array<{ height: number, width: number, fontSize: number, scale: number, el: HTMLElement, dom: HTMLElement }>;

let currRenderDom: string | HTMLElement = null!;
let currelRectification = "";
let currelRectificationLevel: string | number = "";
let currelRectificationIsKeepRatio: string | boolean = "";
let resizeListener: EventListenerOrEventListenerObject = null!;
let timer: number = null!;
let currScale: string | number = 1;
let isElRectification = false;
const autofit: Autofit = {
  isAutofitRunning: false,
  init(options = {}, isShowInitTip = true) {
    if (isShowInitTip) {
      console.log(`autofit.js is running`);
    }
    const {
      dw = 1920,
      dh = 1080,
      el = typeof options === "string" ? options : "body",
      resize = true,
      ignore = [],
      transition = "none",
      delay = 0,
      limit = 0.1,
      cssMode = "scale",
      allowScroll = false,
    } = options as AutofitOption;
    currRenderDom = el;
    const dom = document.querySelector<HTMLElement>(el);
    if (!dom) {
      console.error(`autofit: '${el}' is not exist`);
      return;
    }
    const style = document.createElement("style");
    const ignoreStyle = document.createElement("style");
    style.lang = "text/css";
    ignoreStyle.lang = "text/css";
    style.id = "autofit-style";
    ignoreStyle.id = "ignoreStyle";
    !allowScroll && (style.innerHTML = `body {overflow: hidden;}`);
    const bodyEl = document.querySelector("body")!;
    bodyEl.appendChild(style);
    bodyEl.appendChild(ignoreStyle);
    dom.style.height = `${dh}px`;
    dom.style.width = `${dw}px`;
    dom.style.transformOrigin = `0 0`;
    !allowScroll && (dom.style.overflow = "hidden");
    keepFit(dw, dh, dom, ignore, limit, cssMode);
    resizeListener = () => {
      clearTimeout(timer);
      if (delay != 0)
        timer = setTimeout(() => {
          keepFit(dw, dh, dom, ignore, limit, cssMode);
          isElRectification &&
            elRectification(currelRectification, currelRectificationIsKeepRatio, currelRectificationLevel);
        }, delay) as unknown as number;
      else {
        keepFit(dw, dh, dom, ignore, limit, cssMode);
        isElRectification &&
          elRectification(currelRectification, currelRectificationIsKeepRatio, currelRectificationLevel);
      }
    };
    resize && window.addEventListener("resize", resizeListener);
    this.isAutofitRunning = true;
    setTimeout(() => {
      dom.style.transition = `${transition}s`;
    });
  },
  off(el = "body") {
    try {
      window.removeEventListener("resize", resizeListener);
      const autofitStyle = document.querySelector("#autofit-style");
      autofitStyle && autofitStyle.remove();
      const ignoreStyleDOM = document.querySelector("#ignoreStyle");
      ignoreStyleDOM && ignoreStyleDOM.remove();
      const temp = document.querySelector<HTMLDivElement>(currRenderDom ? currRenderDom as string : el);
      temp && (temp.style.cssText = "")
      isElRectification && offelRectification();
    } catch (error) {
      console.error(`autofit: Failed to remove normally`, error);
    }
    this.isAutofitRunning = false;
    console.log(`autofit.js is off`);
  },
  elRectification: null!,
  scale: currScale
};

function elRectification(el: string, isKeepRatio: string | boolean = true, level: string | number = 1) {
  if (!autofit.isAutofitRunning) {
    console.error("autofit.js：(elRectification): autofit has not been initialized yet");
    return;
  }
  offelRectification();
  !el && console.error(`autofit.js：elRectification bad selector: ${el}`);
  currelRectification = el;
  currelRectificationLevel = level;
  currelRectificationIsKeepRatio = isKeepRatio;
  const currEl = Array.from(document.querySelectorAll<HTMLElement & { originalWidth: number, originalHeight: number }>(el));
  if (currEl.length == 0) {
    console.error(`autofit.js：elRectification found no element by selector: "${el}"`);
    return;
  }
  for (const item of currEl) {
    const rectification = currScale == 1 ? 1 : Number(currScale) * Number(level);
    if (!isElRectification) {
      item.originalWidth = item.clientWidth;
      item.originalHeight = item.clientHeight;
    }
    if (isKeepRatio) {
      item.style.width = `${item.originalWidth * rectification}px`;
      item.style.height = `${item.originalHeight * rectification}px`;
    } else {
      item.style.width = `${100 * rectification}%`;
      item.style.height = `${100 * rectification}%`;
    }
    item.style.transform = `translateZ(0) scale(${1 / Number(currScale)})`;
    item.style.transformOrigin = `0 0`;
  }
  isElRectification = true;
}

function offelRectification() {
  if (!currelRectification) return;
  isElRectification = false;
  for (const item of Array.from(document.querySelectorAll<HTMLElement>(currelRectification))) {
    item.style.width = ``;
    item.style.height = ``;
    item.style.transform = ``;
  }
}
function keepFit(
  dw: number,
  dh: number,
  dom: HTMLElement,
  ignore: AutofitOption['ignore'],
  limit: number,
  cssMode: AutofitOption['cssMode'] = "scale"
) {
  const clientHeight = document.documentElement.clientHeight;
  const clientWidth = document.documentElement.clientWidth;
  currScale =
    clientWidth / clientHeight < dw / dh ? clientWidth / dw : clientHeight / dh;
  currScale = Math.abs(1 - currScale) > limit ? currScale : 1;
  autofit.scale = +currScale;
  const height = Math.round(clientHeight / Number(currScale));
  const width = Math.round(clientWidth / Number(currScale));
  dom.style.height = `${height}px`;
  dom.style.width = `${width}px`;
  if (cssMode === "zoom") {
    (dom.style as any).zoom = `${currScale}`;
  } else {
    dom.style.transform = `translateZ(0) scale(${currScale})`;
  }
  const ignoreStyleDOM = document.querySelector("#ignoreStyle")!;
  ignoreStyleDOM.innerHTML = "";
  for (const temp of ignore!) {
    const item = temp as IgnoreOption & { dom: string };
    let itemEl = item.el || item.dom;
    typeof item == "string" && (itemEl = item);
    if (!itemEl || (typeof itemEl === "object" && !Object.keys(itemEl).length)) {
      console.error(`autofit: found invalid or empty selector/object: ${itemEl}`);
      continue;
    }
    const realScale: number = item.scale ? item.scale : 1 / Number(currScale);
    const realFontSize = realScale != currScale && item.fontSize;
    const realWidth = realScale != currScale && item.width;
    const realHeight = realScale != currScale && item.height;
    ignoreStyleDOM.innerHTML += `\n${itemEl} { 
      transform: scale(${realScale})!important;
      transform-origin: 0 0;
      ${realWidth ? `width: ${realWidth}!important;` : ''}
      ${realHeight ? `height: ${realHeight}!important;` : ''}
    }`;
    if (realFontSize) {
      ignoreStyleDOM.innerHTML += `\n${itemEl} div ,${itemEl} span,${itemEl} a,${itemEl} * {
        font-size: ${realFontSize}px;
      }`;
    }
  }
}
autofit.elRectification = elRectification;
export { autofit as default, elRectification };