import { AbstractCmdController } from "./abstractCmdController";
import { PID, Word } from "../persist";
import { OldSelection } from "./type";
import { TextPolishMode, type Controller } from "./index";
import { uniq } from "lodash-es";
import { findSelections } from "../utils";
import { TranslateMode } from "./translateController";

export interface FoundResult {
  origin: OldSelection[];
  translate: OldSelection[];
  textPolish: OldSelection[];
}

export class SearchReplaceController extends AbstractCmdController {
  private keyword = "";

  private foundResultMap: Record<PID, FoundResult> = {};

  private activeIndex: number | undefined = undefined;

  private isSearchTranslate = true;

  constructor(controller: Controller) {
    super(controller);

    controller.on("paragraphChange", ({ pid }) => {
      if (this.keyword) {
        const foundResult = this.findTextContent(pid, this.keyword);

        if (foundResult.origin.length > 0 || foundResult.translate.length > 0) {
          this.foundResultMap[pid] = foundResult;
        } else {
          delete this.foundResultMap[pid];
        }
        setTimeout(() => {
          this.emitSearchReplaceEvent(false);
        }, 0);
      }
    });
  }

  getFoundSelections(): OldSelection[] {
    const { controller } = this;
    const pids = controller.getPids(true);
    const foundSelections: OldSelection[] = [];
    pids.forEach(pid => {
      const result = this.foundResultMap[pid];
      if (result) {
        if (result.origin.length > 0) {
          foundSelections.push(...result.origin);
        }
        if (result.translate.length > 0) {
          foundSelections.push(...result.translate);
        }
        // 新增改写结果到搜索结果中
        if (result.textPolish.length > 0) {
          foundSelections.push(...result.textPolish);
        }
      }
    });
    return foundSelections;
  }

  getFoundSelectionsByPid(pid: PID): FoundResult | undefined {
    return this.foundResultMap[pid];
  }

  getActiveFoundSelection(): OldSelection | undefined {
    if (this.activeIndex === undefined) {
      return undefined;
    }
    return this.getFoundSelections()[this.activeIndex];
  }

  private findTextContent(pid: PID, _keyword: string): FoundResult {
    const foundResult: FoundResult = {
      origin: [],
      translate: [],
      textPolish: [],
    };
    // 当前翻译模式
    const translateMode = this.controller.getTranslateMode(pid);
    // 当前改写模式
    const textPolishMode = this.controller.getTextPolishMode();

    // 1.原文内容赋值
    const showOrigin =
      [TranslateMode.Disabled, TranslateMode.OriginAndTranslate].includes(translateMode) &&
      [TextPolishMode.Disabled, TextPolishMode.OriginAndTextPolish].includes(textPolishMode);
    if (showOrigin) {
      foundResult.origin = this.findOriginTextContent(pid, _keyword);
    }

    // 2.翻译内容赋值
    const showTranslate = [TranslateMode.OriginAndTranslate, TranslateMode.TranslateOnly].includes(
      translateMode
    );
    if (this.isSearchTranslate && showTranslate) {
      foundResult.translate = this.findTranslateContent(pid, _keyword);
    }

    // 3.改写内容赋值. 备注:this.isSearchTranslate 的判断只是代表是否搜索除原文外的内容，不指代翻译
    const showTextPolish = [
      TextPolishMode.OriginAndTextPolish,
      TextPolishMode.TextPolishOnly,
    ].includes(textPolishMode);
    if (this.isSearchTranslate && showTextPolish) {
      foundResult.textPolish = this.findTextPolishContent(pid, _keyword);
    }
    return foundResult;
  }

  private findOriginTextContent(pid: PID, _keyword: string): OldSelection[] {
    const _textContent = this.controller.getTextContent(pid);
    const textContent = _textContent.toLowerCase();
    const keyword = _keyword.toLowerCase();

    if (!keyword) {
      return [];
    }

    return findSelections(textContent, keyword, {
      pid,
    });
  }

  private findTranslateContent(pid: PID, _keyword: string): OldSelection[] {
    const paragraph = this.controller.getParagraph(pid);

    if (!paragraph) {
      return [];
    }
    let translateWords: Word[] | undefined;
    if (paragraph.isUnfix) {
      const unfixTranslate = this.controller.getUnfixTranslate();
      translateWords = unfixTranslate.fixContentTranslate;
    } else {
      const translate = this.controller.getTranslate(pid);
      if (!translate || translate.translateResult.length === 0) {
        return [];
      }
      translateWords = translate.translateResult;
    }

    const textContent = translateWords
      .reduce((result, word) => {
        return result + word.text;
      }, "")
      .toLowerCase();
    const keyword = _keyword.toLowerCase();

    if (!keyword) {
      return [];
    }

    return findSelections(textContent, keyword, {
      pid,
      readonly: true,
    });
  }

  private findTextPolishContent(pid: PID, _keyword: string): OldSelection[] {
    const paragraph = this.controller.getParagraph(pid);

    if (!paragraph) {
      return [];
    }
    // 通过pid获取到数据 改写段落
    const textPolish = this.controller.getTextPolish(pid);
    if (!textPolish || !textPolish.textPolishResult) {
      return [];
    }
    // 所有数据转换成 小写
    const textContent = textPolish.textPolishResult.formal_paragraph.toLowerCase();
    const keyword = _keyword.toLowerCase();
    //  若没有段落内容，返回 []
    if (!keyword) {
      return [];
    }

    return findSelections(textContent, keyword, {
      pid,
      readonly: true,
    });
  }

  private find(keyword: string, isSearchTranslate = true) {
    const { controller } = this;
    this.keyword = keyword;
    this.isSearchTranslate = isSearchTranslate;
    const pids = controller.getPids(true);
    this.foundResultMap = {};
    pids.forEach(pid => {
      const foundeds = this.findTextContent(pid, keyword);
      if (
        foundeds.origin.length > 0 ||
        foundeds.translate.length > 0 ||
        foundeds.textPolish.length > 0
      ) {
        this.foundResultMap[pid] = foundeds;
      }
    });
  }

  findAndMark(keyword: string, isSearchTranslate = true, enableScroll = true) {
    let maybeChangedPids = Object.keys(this.foundResultMap);
    this.find(keyword, isSearchTranslate);
    const selectionLength = this.getFoundSelections().length;
    if (selectionLength > 0) {
      if (!this.activeIndex || selectionLength <= this.activeIndex) {
        this.activeIndex = 0;
      }
    } else {
      this.activeIndex = undefined;
    }

    maybeChangedPids = uniq(maybeChangedPids.concat(...Object.keys(this.foundResultMap)));

    this.controller.emit("paragraphListChange", {
      maybeChangedPids,
    });

    this.emitSearchReplaceEvent(enableScroll);

    return this.foundResultMap;
  }

  reFindAndMark() {
    if (this.keyword) {
      this.findAndMark(this.keyword, this.isSearchTranslate, false);
    }
  }

  generateFoundPrevNext(type: "prev" | "next") {
    const selections = this.getFoundSelections();
    const maybeChangedPids: PID[] = [];
    if (this.activeIndex !== undefined) {
      maybeChangedPids.push(selections[this.activeIndex].pid);
    }
    if (this.activeIndex === undefined) {
      this.activeIndex = 0;
    }

    if (type === "prev") {
      if (this.activeIndex === 0) {
        this.activeIndex = selections.length - 1;
      } else {
        this.activeIndex--;
      }
    } else if (type === "next") {
      if (this.activeIndex + 1 === selections.length) {
        this.activeIndex = 0;
      } else {
        this.activeIndex++;
      }
    }

    maybeChangedPids.push(selections[this.activeIndex].pid);
    this.controller.emit("paragraphListChange", {
      maybeChangedPids,
    });
    this.emitSearchReplaceEvent();
  }

  prev() {
    this.generateFoundPrevNext("prev");
  }

  next() {
    this.generateFoundPrevNext("next");
  }

  replace(text: string) {
    if (this.activeIndex === undefined) {
      return;
    }
    const selections = this.getFoundSelections();

    const { length } = selections;

    if (length && length === this.activeIndex) {
      this.activeIndex -= 1;
    }

    const selection = selections[this.activeIndex];
    const nextSelection = selections[this.activeIndex + 1];
    const prevSelection = selections[this.activeIndex - 1];

    if (selection && selection.readonly) {
      return;
    }

    this.controller
      .dontSelectNext()
      .replaceText(selection.pid, selection.startOffset, selection.endOffset, text);

    const maybeChangedPids = [selection.pid];
    if (nextSelection === undefined) {
      this.activeIndex = length - 1;
      if (prevSelection) {
        maybeChangedPids.push(prevSelection.pid);
      }
    } else {
      maybeChangedPids.push(nextSelection.pid);
    }

    this.controller.emit("paragraphListChange", {
      maybeChangedPids,
    });
    this.emitSearchReplaceEvent();
  }

  replaceAll(text: string) {
    const maybeChangedPids = Object.keys(this.foundResultMap);
    const pLength = maybeChangedPids.length;
    if (pLength === 0) {
      return;
    }
    this.controller.dontSelectNext();
    let pIndex = 0;
    for (const [, foundResult] of Object.entries(this.foundResultMap)) {
      const { origin } = foundResult;
      let offset = 0;
      const selectionLength = origin.length;
      for (let i = 0; i < selectionLength; i++) {
        const selection = origin[i];
        this.controller.replaceText(
          selection.pid,
          selection.startOffset + offset,
          selection.endOffset + offset,
          text,
          !(pIndex + 1 === pLength && i + 1 === selectionLength)
        );
        offset += text.length - (selection.endOffset - selection.startOffset);
      }
      pIndex++;
    }

    this.reFindAndMark();
    this.controller.emit("paragraphListChange", {
      maybeChangedPids,
    });
    this.emitSearchReplaceEvent();
  }

  emitSearchReplaceEvent(enableScroll = true) {
    const selections = this.getFoundSelections();
    this.controller.emit("searchReplaceChange", {
      keyword: this.keyword,
      activeIndex: this.activeIndex ?? 0,
      foundLength: selections.length,
      selections,
      enableScroll,
    });
  }

  resetFind() {
    const maybeChangedPids = Object.keys(this.foundResultMap);

    this.keyword = "";
    this.activeIndex = undefined;
    this.foundResultMap = {};
    this.controller.emit("paragraphListChange", {
      maybeChangedPids,
    });

    this.emitSearchReplaceEvent();
  }

  reset() {
    this.keyword = "";
    this.activeIndex = undefined;
    this.isSearchTranslate = true;
    this.foundResultMap = {};
  }
}
