import { Controller, PID, Word } from "@tingwujs/core";
import type { TranscriptionController } from "../controller";
import { getFormatText } from "./format";

/**
 * Sample:
 *
 * calcCustomHeight()
 *  .getContainer((container) => {
 *    container.style.width = '100px';
 *  })
 *  .andWidth(100)
 *  .andText('testsdfjsdklfj')
 *  .getHeight(122);
 */
export const calcCustomHeight = () => {
  return {
    getElement: (callback: (c: HTMLElement) => void) => {
      const id = "custom-calc-content";
      let container = document.getElementById(id);
      if (!container) {
        container = document.createElement("div");
        container.id = id;
        container.style.background = "#FFFFFF";
        container.style.zIndex = "9999";
        container.style.position = "absolute";
        container.style.top = "-9999px";
        container.style.left = "-9999px";
        container.style.visibility = "hidden";
        document.body.append(container);
      }

      let content = container!.querySelector("div");
      if (!content) {
        content = document.createElement("div");
        container.append(content);
      }

      content.removeAttribute("style");
      callback(content);

      return {
        andWidth: (width: number) => {
          container!.style.width = `${width}px`;

          return {
            andNodes: (nodes: Array<Text | HTMLElement>) => {
              if (content) {
                content.innerHTML = "";
              }
              nodes.forEach(node => {
                content?.appendChild(node);
              });
              return {
                getHeight: (added = 0) => {
                  return container!.clientHeight + added;
                },
              };
            },
          };
        },
      };
    },
  };
};

export interface CalcParagraphHeightParams {
  controller: Controller;
  transController: TranscriptionController;
  pid: PID;
  theme: any;
  livingMode: boolean;
  readonly: boolean;
  showOrigin?: boolean;
  showTranslate?: boolean;
  showTextPolish?: boolean;
  containerWidth: number;
}

const prepareContentCalc = (params: CalcParagraphHeightParams) => {
  const id = "calc-content";
  let container = document.getElementById(id);
  if (!container) {
    container = document.createElement("div");
    container.id = id;
    container.style.boxSizing = "border-box";
    container.style.background = "#FFFFFF";
    container.style.zIndex = "9999";
    container.style.position = "absolute";
    container.style.top = "-9999px";
    container.style.left = "-9999px";
    container.style.visibility = "hidden";
    container.style.fontSize = "14px";
    container.style.lineHeight = "24px";
    container.style.fontFamily = params.theme.editorFontFamily;
    container.style.overflowWrap = "break-word";
    container.style.wordBreak = "break-word";
    container.style.whiteSpace = "pre-wrap";

    const content = document.createElement("div");
    content.style.minHeight = "24px";
    container.append(content);

    (document.querySelector(".appRoot") || document.body).append(container);
  }

  return container;
};

export const getFormatedElements = (words: Word[]) => {
  const fragment = document.createDocumentFragment();
  words.forEach(word => {
    const span = document.createElement("span");
    span.style.overflowWrap = "anywhere";
    span.textContent = getFormatText(word.text);
    fragment.append(span);
  });
  return fragment;
};

export const replaceWords = (container: HTMLElement, words: Word[]) => {
  container.innerHTML = "";
  container.appendChild(getFormatedElements(words));
};

export const calcParagraphHeight = (params: CalcParagraphHeightParams) => {
  const {
    controller,
    pid,
    theme,
    showOrigin = true,
    showTranslate = false,
    showTextPolish = false,
    containerWidth,
  } = params;

  let localContainerWidth = containerWidth;
  // localContainerWidth -= 4; // 4 is border and padding for paragraphContent

  const headerHeight = 24 + 8;

  const container = prepareContentCalc(params);
  const content = container.querySelector("div")!;

  container.style.paddingLeft = `${theme.paragraphHorizontalPadding}px`;
  container.style.paddingRight = `${theme.paragraphHorizontalPadding}px`;

  const contentHeight = (() => {
    if (!showOrigin) {
      return 0;
    }
    const words = controller.getWords(pid);
    container.style.width = `${localContainerWidth}px`;
    container.style.paddingTop = `${theme.paragraphVerticalPadding}px`;
    if (showTranslate) {
      container.style.paddingBottom = "8px";
    } else {
      container.style.paddingBottom = `${theme.paragraphVerticalPadding}px`;
    }

    replaceWords(content, words);

    return container.clientHeight;
  })();

  const translateHeight = (() => {
    if (!showTranslate) {
      return 0;
    }
    const translate = controller.getTranslate(pid);
    if (!translate) {
      return 0;
    }
    container.style.width = `${localContainerWidth}px`;
    if (showOrigin) {
      container.style.paddingTop = "8px";
    } else {
      container.style.paddingTop = `${theme.paragraphVerticalPadding}px`;
    }
    container.style.paddingBottom = `${theme.paragraphVerticalPadding}px`;
    replaceWords(content, translate.translateResult);
    return container.clientHeight;
  })();

  // AI改写高度
  const textPolishHeight = (() => {
    if (!showTextPolish) {
      return 0;
    }
    const textPolish = controller.getTextPolish(pid);
    if (!textPolish) {
      return 0;
    }
    container.style.width = `${localContainerWidth}px`;
    if (showOrigin) {
      container.style.paddingTop = "8px";
    } else {
      container.style.paddingTop = `${theme.paragraphVerticalPadding}px`;
    }
    container.style.paddingBottom = `${theme.paragraphVerticalPadding}px`;
    replaceWords(content, [
      {
        wid: "",
        sentenceId: "",
        beginTime: 0,
        endTime: 0,
        tag: "",
        text: textPolish.textPolishResult.formal_paragraph,
      },
    ]);
    return container.clientHeight + 20; // 20是AI改写标记高度
  })();

  const extraHeight = 4; // 4 is border and padding for paragraphContent
  const paragraphHeight =
    headerHeight + extraHeight + Math.max(translateHeight + contentHeight + textPolishHeight, 56);

  return paragraphHeight;
};
