import { Eventer } from "@tingwu/common";
import {
  TranModel,
  PID,
  WID,
  Paragraph,
  Transcription,
  Word,
  P,
  SID,
  // ISc,
} from "../persist";
import { getWordBySc } from "../utils";

function genDefaultTranscription(): Transcription {
  return {
    paragraphs: {},
    words: {},
    pids: [],
    appearedWids: {},
    userChangedSidsMap: {},
  };
}

export interface BookEventMap {
  wordChange: {
    pid: PID;
    wid: WID;
    word: Word;
    userChangeText?: Boolean;
  };
  paragraphChange: {
    pid: PID;
    wid?: WID;
    typeChange?: string; // 拓展 改变文段的类型
    tempDeleteWord?: object; // 删除的词内容
    oldParagraph?: Paragraph; // 修改前的段落
  };
  paragraphListChange: {
    maybeChangedPids: PID[];
  };
  paragraphChangeSpecific: {
    addParagraphPids: PID[];
    removeParagraphPids: PID[];
    updateParagraphPids: PID[];
    removeParagraphs?: Record<PID, Paragraph>;
    textNotChangePids?: PID[];
    userChangePids?: PID[];
  };
  lastUnfixPidChange: {
    pid: PID;
  };
  zyEditor: {
    type: string;
    data: any;
  };
}

export class Book extends Eventer<BookEventMap> {
  public transcription: Transcription = genDefaultTranscription();

  public paragraphVersions: Record<PID, number> = {};

  private uniqueId = 0;

  private collectLivingFixWords: any = {};

  updateParagraphVersion(...pids: PID[]) {
    pids.forEach(pid => {
      this.paragraphVersions[pid] = ++this.uniqueId;
    });
  }

  resetModelValue() {
    this.transcription = genDefaultTranscription();
    this.paragraphVersions = {};
    this.uniqueId = 0;
  }

  updateWord(
    pid: PID,
    wid: WID,
    word: Word,
    userChangeText?: boolean,
    textNotChangePids: PID[] = []
  ) {
    this.transcription.words[wid] = word;

    if (userChangeText) {
      this.transcription.userChangedSidsMap[word.sentenceId] = true;
    }

    this.updateParagraphVersion(pid);

    this.emit("wordChange", {
      pid,
      wid,
      word,
      userChangeText,
    });

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [pid],
      removeParagraphPids: [],
      textNotChangePids,
    });
  }

  updateWords(pid: PID, words: Word[], textNotChangePids: PID[] = []) {
    words.forEach(word => {
      this.transcription.words[word.wid] = word;
    });

    this.updateParagraphVersion(pid);

    this.emit("paragraphChange", {
      pid,
    });

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [pid],
      removeParagraphPids: [],
      textNotChangePids,
    });
  }

  deleteWord(pid: PID, wid: WID, newOrder: WID[]) {
    const word = this.transcription.words[wid];
    const tempDelete = {
      // 重组数据 符合接口
      id: Number(word.wid),
      bt: word.beginTime,
      et: word.endTime,
      si: Number(word.sentenceId),
      tc: word.text,
    };
    delete this.transcription.words[wid];
    this.transcription.paragraphs[pid].wids = newOrder;

    this.updateParagraphVersion(pid);

    this.emit("paragraphChange", {
      pid,
      wid,
      typeChange: "deleteWord",
      tempDeleteWord: tempDelete,
    });

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [pid],
      removeParagraphPids: [],
    });
  }

  addWord(pid: PID, word: Word, newOrder: WID[]) {
    this.transcription.words[word.wid] = word;
    this.transcription.paragraphs[pid].wids = newOrder;
    this.transcription.appearedWids[word.wid] = true;

    const tempDelete = {
      // 重组数据 符合接口
      id: Number(word.wid),
      bt: word.beginTime,
      et: word.endTime,
      si: Number(word.sentenceId),
      tc: word.text,
    };

    this.updateParagraphVersion(pid);

    this.emit("paragraphChange", {
      pid,
      tempDeleteWord: tempDelete,
      wid: word.wid,
      typeChange: "addWord",
    });

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [pid],
      removeParagraphPids: [],
    });
  }

  isAppearedWid(wid: WID) {
    return this.transcription.appearedWids[wid] ?? false;
  }

  moveWord(originPid: PID, originNewOrder: WID[], targetPid: PID, targetNewOrder: WID[]) {
    this.transcription.paragraphs[originPid].wids = originNewOrder;
    this.transcription.paragraphs[targetPid].wids = targetNewOrder;

    this.updateParagraphVersion(originPid, targetPid);

    this.emit("paragraphChange", {
      pid: originPid,
    });
    this.emit("paragraphChange", {
      pid: targetPid,
    });

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [originPid, targetPid],
      removeParagraphPids: [],
    });
  }

  addParagraph(prevPid: PID | undefined, pid: PID, paragraph: Paragraph) {
    const { pids } = this.transcription;
    if (prevPid === undefined) {
      pids.unshift(pid);
    } else {
      const pos = pids.indexOf(prevPid);
      if (pos !== -1) {
        pids.splice(pos + 1, 0, pid);
      }
    }

    this.transcription.paragraphs[pid] = paragraph;

    const maybeChangedPids = prevPid ? [prevPid, pid] : [pid];

    this.updateParagraphVersion(pid);

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

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [pid],
      updateParagraphPids: [],
      removeParagraphPids: [],
    });
  }

  removeParagraph(pid: PID) {
    const { pids, paragraphs } = this.transcription;
    const willDeletedParagraph = paragraphs[pid];
    const pos = pids.indexOf(pid);
    const prevPid = pids[pos - 1];
    const nextPid = pids[pos + 1];

    if (pos !== -1) {
      pids.splice(pos, 1);
    }
    delete paragraphs[pid];

    const maybeChangedPids = [prevPid, nextPid].filter(i => i != null);

    this.updateParagraphVersion(...maybeChangedPids, pid);

    this.emit("paragraphListChange", {
      maybeChangedPids,
    });
    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [],
      removeParagraphPids: [pid],
      removeParagraphs: {
        [pid]: willDeletedParagraph,
      },
    });
  }

  updateParagraph(pid: PID, paragraph: Paragraph, userChangePids: PID[] = []) {
    const oldParagraph = this.transcription.paragraphs[pid];
    this.transcription.paragraphs[pid] = paragraph;

    this.updateParagraphVersion(pid);
    this.emit("paragraphChange", {
      pid,
      oldParagraph,
    });

    if (oldParagraph && oldParagraph.isUnfix && !paragraph.isUnfix) {
      this.emit("lastUnfixPidChange", { pid });
    }

    this.emit("paragraphChangeSpecific", {
      addParagraphPids: [],
      updateParagraphPids: [pid],
      removeParagraphPids: [],
      userChangePids,
    });
  }

  getWordBySc(_sc: P["sc"][0]): Word {
    return getWordBySc(_sc);
  }

  getPByPid(pid: PID, wid?: WID): P | undefined {
    const paragraph = this.transcription.paragraphs[pid];
    const allWords = this.transcription.words;

    if (!paragraph) {
      return undefined;
    }
    this.collectLivingFixWords = {};
    const words = paragraph.wids.map(wid => allWords[wid]);
    const sc = words.map(word => {
      const _obj = {
        id: Number(word.wid),
        bt: word.beginTime,
        et: word.endTime,
        si: Number(word.sentenceId),
        tc: word.text,
      };
      if (wid === word.wid) {
        this.collectLivingFixWords = _obj;
      }
      return {
        ..._obj,
        ...(word.tag
          ? {
              tag: {
                mark: word.tag,
              },
            }
          : {}),
      };
    });

    return {
      pi: pid,
      ui: paragraph.uid,
      sc,
      fixText: this.collectLivingFixWords,
    };
  }

  isUserChangedSentence(sid: SID) {
    return this.transcription.userChangedSidsMap[sid] ?? false;
  }

  appendHistory(model: TranModel) {
    const paragraphs: Record<PID, Paragraph> = {};
    const pids: PID[] = [];
    const words: Record<WID, Word> = {};

    model.pg.forEach(_pg => {
      if (pids.indexOf(_pg.pi) > -1) {
        // 避免重复PID导致听悟后续流程错误，重复PID检测目前在应用层，这里暂时不做上报
        return;
      }
      const wids: WID[] = [];

      _pg.sc.forEach(_sc => {
        const word = this.getWordBySc(_sc);
        words[word.wid] = word;
        wids.push(word.wid);
      });
      paragraphs[_pg.pi] = {
        pid: _pg.pi,
        uid: _pg.ui,
        wids,
      };
      pids.push(_pg.pi);
    });

    this.transcription.paragraphs = {
      ...paragraphs,
      ...this.transcription.paragraphs,
    };
    this.transcription.words = {
      ...words,
      ...this.transcription.words,
    };
    // 需要包装顺序
    this.transcription.pids = [...pids, ...this.transcription.pids];
  }

  fromJSON(model: TranModel) {
    const paragraphs: Record<PID, Paragraph> = {};
    const pids: PID[] = [];
    const words: Record<WID, Word> = {};

    model.pg.forEach(_pg => {
      if (pids.indexOf(_pg.pi) > -1) {
        // 避免重复PID导致听悟后续流程错误，重复PID检测目前在应用层，这里暂时不做上报
        return;
      }
      const wids: WID[] = [];

      _pg.sc.forEach(_sc => {
        const word = this.getWordBySc(_sc);
        words[word.wid] = word;
        wids.push(word.wid);
      });
      paragraphs[_pg.pi] = {
        pid: _pg.pi,
        uid: _pg.ui,
        wids,
      };
      pids.push(_pg.pi);
    });

    this.transcription = {
      paragraphs,
      pids,
      words,
      appearedWids: {},
      userChangedSidsMap: {},
    };
  }

  toJSON(): TranModel {
    const pg: P[] = [];
    this.transcription.pids.forEach(pid => {
      const p = this.getPByPid(pid);
      if (p) {
        pg.push(p);
      }
    });
    return {
      pg,
    };
  }
}
