import { indexOf } from "lodash-es";
import classnames from "classnames";
import { WID, Word } from "@tingwujs/core";
import { AnyPatch, ViewedWord } from "../model";
import { getFormatText } from "./format";

const replaceChildren = (container: HTMLElement, fragment: Element | DocumentFragment) => {
  if (container.replaceChildren) {
    container.replaceChildren(fragment);
  } else {
    container.innerHTML = "";
    container.appendChild(fragment);
  }
};

const renderTextWithSearchedOffsets = (word: ViewedWord) => {
  const { text, searchedOffsets } = word;
  let pos = 0;
  const fragment = document.createDocumentFragment();
  const _text = getFormatText(text);

  searchedOffsets!.forEach(range => {
    const { startOffset, endOffset, isActive } = range;
    if (pos < startOffset) {
      fragment.appendChild(document.createTextNode(_text.slice(pos, startOffset)));
    }

    const span = document.createElement("span");
    span.className = isActive ? "searched actived" : "searched";
    span.textContent = _text.slice(startOffset, endOffset);

    fragment.appendChild(span);
    pos = endOffset;
  });

  if (pos < _text.length) {
    fragment.appendChild(document.createTextNode(_text.slice(pos, _text.length)));
  }

  return fragment;
};

const generateViewedWordDOM = (word: ViewedWord, target?: HTMLElement) => {
  const span = target || document.createElement("span");
  span.setAttribute("data-word-id", word.wid);

  const cls = classnames({
    strong: word.isAgendaHighlight || word.isTodoListHighlight,
    played: word.isPlayed,
    base: !word.isPlayed,
    qna: word.qnaModel === "question" ? !word.isQnaActive && word.isQna : undefined, // TODO QA data2
    actived: word.qnaModel === "question" ? word.isQnaActive : undefined,
    "word-blue": word.tag === "blue",
    "word-yellow": word.tag === "yellow",
    "word-red": word.tag === "red",
  });
  span.className = cls;

  span.innerHTML = "";
  if (word.searchedOffsets && word.searchedOffsets.length > 0) {
    span.appendChild(renderTextWithSearchedOffsets(word));
  } else {
    span.textContent = getFormatText(word.text);
  }
  return span;
};

export const updateAllWords = (element: HTMLElement, words: ViewedWord[]) => {
  const fragment = document.createDocumentFragment();
  if (words.length === 1 && words[0].text === "") {
    if (element.firstChild?.nodeType === Node.TEXT_NODE) {
      return;
    }
    fragment.appendChild(document.createTextNode(""));
  } else {
    words.forEach(word => {
      const span = generateViewedWordDOM(word);
      fragment.appendChild(span);
    });
  }

  replaceChildren(element, fragment);
};

const deleteAfterWords = (element: HTMLElement, wid: WID) => {
  const target = element.querySelector(`[data-word-id="${wid}"]`);

  if (!target && element.firstChild?.nodeType === Node.TEXT_NODE) {
    while (element.firstChild) {
      element.removeChild(element.firstChild);
    }
    return;
  }
  const pos = indexOf(element.childNodes, target);
  if (pos > -1) {
    const { length } = element.childNodes;
    let lastPos = length - 1;
    while (lastPos >= pos) {
      element.childNodes[lastPos].remove();
      lastPos--;
    }
  }
};

const appendWords = (element: HTMLElement, words: ViewedWord[]) => {
  const fragment = document.createDocumentFragment();

  words.forEach(word => {
    const span = generateViewedWordDOM(word);
    fragment.appendChild(span);
  });

  element.appendChild(fragment);
};

const updateWord = (element: HTMLElement, wid: WID, word: ViewedWord) => {
  const target = element.querySelector(`[data-word-id="${wid}"]`);
  if (!target && element.firstChild?.nodeType === Node.TEXT_NODE) {
    replaceChildren(element, generateViewedWordDOM(word));
  } else {
    generateViewedWordDOM(word, target as HTMLElement);
  }
};

export const renderWordsTextContent = (viewedWords: ViewedWord[]) => {
  const fragment = document.createDocumentFragment();

  if (viewedWords.length === 1 && viewedWords[0].text === "") {
    fragment.appendChild(document.createTextNode(""));
  } else {
    viewedWords.forEach(word => {
      const span = document.createElement("span");
      span.setAttribute("data-word-id", word.wid);
      if (word.isPlayed) {
        span.className = "played";
      }

      if (word.tag) {
        switch (word.tag) {
          case "blue":
            span.className = "word-blue";
            break;
          case "yellow":
            span.className = "word-yellow";
            break;
          case "red":
            span.className = "word-red";
            break;
          default:
            break;
        }
      }

      if (word.searchedOffsets && word.searchedOffsets.length > 0) {
        span.appendChild(renderTextWithSearchedOffsets(word));
      } else {
        span.textContent = getFormatText(word.text);
      }

      fragment.appendChild(span);
    });
  }
  return fragment;
};

const removeUselessTextNode = (element: HTMLElement) => {
  const toRemoveNodes: ChildNode[] = [];
  element.childNodes.forEach(node => {
    if (node.nodeType === Node.TEXT_NODE) {
      toRemoveNodes.push(node);
    }
  });
  if (toRemoveNodes.length < element.childNodes.length) {
    toRemoveNodes.forEach(node => {
      element.removeChild(node);
    });
  }
};

export const patch = (element: HTMLElement, patches: AnyPatch[]) => {
  if (patches.length === 0) {
    return;
  }

  patches.forEach(p => {
    const [type, payload] = p;
    switch (type) {
      case "updateAllWords":
        updateAllWords(element, payload.words);
        break;
      case "deleteAfterWords":
        deleteAfterWords(element, payload.wid);
        break;
      case "appendWords":
        appendWords(element, payload.words);
        break;
      case "updateWord":
        updateWord(element, payload.wid, payload.word);
        break;
      default:
        break;
    }
  });

  removeUselessTextNode(element);
};

export const reviseTextContent = (element: HTMLElement, words: Word[]) => {
  words.forEach(word => {
    const target = element.querySelector(`[data-word-id="${word.wid}"]`);
    if (!target) {
      return;
    }
    if (target.textContent !== word.text) {
      target.textContent = word.text;
    }
  });
};

// 暂时屏蔽局部分享
export const isPartialShareFeature = false;
