import { throttle, assign } from "lodash-es";
import {
  Controller,
  PID,
  SID,
  Eventes,
  TranslateMode,
  TranscriptingState,
  getSidWidsMap,
  report,
} from "@tingwu/core";
import {
  getSentenseByWords,
  getTextContentBySentence,
  generateTempTranslateWords,
} from "./sentense";

export type TranlateCallback = (needTranslates: string[]) => Promise<string[]>;

export interface UnfixTranslateThrottlerOptions {
  alwaysTrigger?: boolean;
}

const getDefaultOptions = () => {
  return {
    alwaysTrigger: false,
  };
};

export const createUnfixTranslateThrottler = (
  controller: Controller,
  callback: TranlateCallback,
  options: UnfixTranslateThrottlerOptions = {}
) => {
  // 单unfix模式，不支持多个unfix同时并存
  let storedPid: PID | undefined; // undefined means emptyUnfix
  let store: Record<SID, { textContent: string; translate: string }> = {};
  let callbackId = 0;
  let applyedId = 0;

  const realOptions = assign(getDefaultOptions(), options);

  const eventHandler = throttle(async (args: Eventes["unfixContentChange"]) => {
    if (controller.getTranscriptingState() === TranscriptingState.Disabled) {
      return;
    }
    if (!realOptions.alwaysTrigger && controller.getTranslateMode() === TranslateMode.Disabled) {
      return;
    }
    const holdCallbackId = ++callbackId;
    const { unfixContent, fixWords, pid } = args;
    if (storedPid !== pid) {
      storedPid = pid;
      store = {};
    }
    const sentences = getSentenseByWords(fixWords);
    const { length } = sentences;
    const needTranslateSentences = [];

    const { sidWidsMap } = getSidWidsMap(
      controller,
      fixWords.map(w => w.wid)
    );

    let nfixingContent = "";
    for (let i = 0; i < length; i++) {
      const sentence = sentences[i];
      const textContent = getTextContentBySentence(sentence);
      if (store[sentence.sid] && store[sentence.sid].textContent === textContent) {
        continue;
      }
      if (sidWidsMap[sentence.sid]?.isNfixing) {
        nfixingContent += getTextContentBySentence(sentence);
      } else {
        needTranslateSentences.push(sentence);
      }
    }
    try {
      const input = needTranslateSentences.map(sentence => getTextContentBySentence(sentence));

      const isHasUnfixContent = Boolean(nfixingContent || unfixContent);

      if (isHasUnfixContent) {
        input.push(nfixingContent + unfixContent);
      }

      const translateResult = input.length > 0 ? await callback(input) : [];

      if (controller.getTranscriptingState() === TranscriptingState.Disabled) {
        return;
      }

      report("print translate", {
        needTranslateSentences,
        input,
        output: translateResult,
      });

      const unfixToFixedPid = controller.getUnfixToFixedPid();
      // 在翻译结果回来的时候，当前段落变成fix应该忽略掉这次翻译
      if (holdCallbackId < applyedId || (unfixToFixedPid && unfixToFixedPid === pid)) {
        return;
      }

      applyedId = holdCallbackId;

      if (translateResult.length !== input.length) {
        console.error("Translate output cannot match input.", input, translateResult);
        return;
      }

      needTranslateSentences.forEach((sentence, index) => {
        store[sentence.sid] = {
          textContent: getTextContentBySentence(sentence),
          translate: translateResult[index],
        };
      });

      const unfixContentTranslate = isHasUnfixContent
        ? translateResult[translateResult.length - 1]
        : "";

      const words = generateTempTranslateWords(sentences, sentence => {
        const found = store[sentence.sid];
        return found ? found.translate : "";
      });

      controller.setUnfixTranslateByWords(pid, unfixContentTranslate, words);
    } catch (error) {
      console.error("Translate server maybe crush.", error);
      throw error;
    }
  }, 1000);

  return controller.on("unfixContentChange", eventHandler);
};
