import {
  LisioBooleanParameterNames,
  LisioStringParameterNames,
} from "@lisio/lisio-profils";

import { LisioParamsController } from "../controllers/lisio-params-controller";
import LisioStyleSheetController from "../controllers/lisio-stylesheet-controller";
import LisioCSSProperties from "../enums/lisio-css-properties";
import LisioTextTreeWalker from "../walkers/lisio-text-tree-walker";
import LisioAdapter from "./lisio-adapter";

/**
 * Class representing a dyslexia adapter extending an adapter.\
 * It aims to represents the font style functionality of Lisio.\
 * A dyslexia adapter is basically a functionality of Lisio which will change the dyslexia of texts in the main page.\
 */
class LisioDyslexiaAdapter extends LisioAdapter<boolean | string> {
  private _nodes: HTMLElement[] = [];
  private _params: LisioParamsController;

  constructor(params: LisioParamsController) {
    super();
    this._params = params;
  }

  public get nodes() {
    return this._nodes;
  }

  /**
   * Public method implementing abstract method adapt of Adapter
   * @param {LisioTextTreeWalker} walker - A walker to explore the DOM
   * @param {boolean | string} value - Value of the functionality
   * @returns Returns nothing
   * @source
   */
  public adapt(walker: LisioTextTreeWalker, value: boolean | string): void {
    const isActive =
      typeof value === "string"
        ? (this._params.params.get(
            LisioBooleanParameterNames.ACTIVE_UNDERLINE,
          ) as boolean) || false
        : value;
    const lettersToRemove = document.querySelectorAll<HTMLElement>(
      `[data-lisio-letter].r,[data-lisio-letter].b,[data-lisio-letter].g`,
    );
    for (const letterToRemove of lettersToRemove) {
      this.deleteLetter(letterToRemove);
    }
    const red = this._params.params.get(
      LisioStringParameterNames.UNDERLINE_RED,
    ) as string | undefined;
    const green = this._params.params.get(
      LisioStringParameterNames.UNDERLINE_GREEN,
    ) as string | undefined;
    const blue = this._params.params.get(
      LisioStringParameterNames.UNDERLINE_BLUE,
    ) as string | undefined;
    const colors = [
      [LisioStringParameterNames.UNDERLINE_RED, red],
      [LisioStringParameterNames.UNDERLINE_GREEN, green],
      [LisioStringParameterNames.UNDERLINE_BLUE, blue],
    ];
    if (isActive && (red != "" || green != "" || blue != "")) {
      LisioStyleSheetController.current.insertRule(
        ".underline-red",
        new Map<LisioCSSProperties, string>([
          [LisioCSSProperties.BACKGROUND_COLOR, "#e68989"],
        ]),
      );
      LisioStyleSheetController.current.insertRule(
        ".underline-green",
        new Map<LisioCSSProperties, string>([
          [LisioCSSProperties.BACKGROUND_COLOR, "#bae28e"],
        ]),
      );
      LisioStyleSheetController.current.insertRule(
        ".underline-blue",
        new Map<LisioCSSProperties, string>([
          [LisioCSSProperties.BACKGROUND_COLOR, "#86a5ec"],
        ]),
      );
      for (const tag of walker.tags.values()) {
        //txt
        this.adaptFunction(tag, isActive);
      }

      for (const [key, values] of colors) {
        //pour chaque couleurs
        if (values != undefined && key != undefined && values != "") {
          //si la valeur n'est pas vide
          for (const letter of values.split(" ")) {
            const letters = document.querySelectorAll<HTMLElement>(
              `[data-lisio-letter="${letter}"]`,
            ); //on récupère tous les letters crée par la fonctionnalité qui contiennent cette lettre
            for (const letter of letters) {
              letter.className = key.replace("_", "-");
            }
          }
        }
      }

      const letters = document.querySelectorAll<HTMLElement>(
        "[data-lisio-letter]",
      );
      if (letters != null) {
        const oldLetters: string[] = [];
        for (const letter of letters) {
          const letterTxt = letter.getAttribute("data-lisio-letter");
          if (letterTxt != undefined && oldLetters.indexOf(letterTxt) == -1) {
            oldLetters.push(letterTxt);
          }
        }

        const newLetters: string[] = [];
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        for (const [_, values] of colors) {
          if (values != undefined && values != "") {
            for (const value of values.split(" ")) {
              newLetters.push(value.toLocaleLowerCase());
            }
          }
        }

        const supprs = oldLetters.filter((letterTxt) => {
          return !newLetters.includes(letterTxt.toLocaleLowerCase());
        });
        for (const suppr of supprs) {
          document
            .querySelectorAll<HTMLElement>(`[data-lisio-letter=${suppr}]`)
            .forEach((elt) => {
              //on récupère tous les noeuds créées par la solution
              this.deleteLetter(elt);
            });
        }

        for (let index = this._nodes.length - 1; index > 0; index--) {
          //on parcours le node en partant de la fin
          const cloneNode = this._nodes[index].cloneNode(true) as HTMLElement; //on clone le noeud ainsi que tous ses noeuds enfants
          for (let index2 = 0; index2 < cloneNode.childNodes.length; index2++) {
            //on boucle sur tous les noeuds enfant du noeud cloné
            if (cloneNode.childNodes[index2].nodeType == 3) {
              //si le noeud en question est un noeud texte on continue le traitement
              for (const [key, values] of colors) {
                //pour chaque couleur
                if (values != undefined && values != "") {
                  //si la couleur n'est pas vide
                  for (const value of values.split(" ")) {
                    const nodeCompl =
                      cloneNode.childNodes.length > 1 ? true : false; //si le noeud possède plusieurs on passe true sinon on reste à false
                    if (cloneNode.childNodes[index2].nodeType == 3) {
                      //on vérifie si le noeud est toujours un noeud texte
                      const insertBefore =
                        cloneNode.childNodes[index2].nextSibling; //noeud de référence pour l'insertion
                      const regExp = RegExp(value, "gi");
                      let exec;
                      let i = 0;
                      let find = false; //première correspondance a été trouvé ? false
                      const base = cloneNode.childNodes[index2].nodeValue || ""; //texte de base
                      while ((exec = regExp.exec(base)) != undefined) {
                        //tant qu'il y a des correspondance dans la chaine de carac
                        if (!find) {
                          //si aucune correspondance a été trouvée
                          i = exec["index"]; //index de la lettre trouvée
                          cloneNode.replaceChild(
                            document.createTextNode(base.slice(0, i)),
                            cloneNode.childNodes[index2],
                          ); //on ne garde que la partie avant la lettre
                        } else {
                          //si y a déjà eu une correspondance
                          if (insertBefore != undefined && nodeCompl) {
                            //si on a un noeud de référence et que le neoud est complexe
                            cloneNode.insertBefore(
                              document.createTextNode(
                                base.slice(i + value.length, exec["index"]),
                              ),
                              insertBefore,
                            ); //on insère le texte depuis la dernière correspondance jusqu'à la nouvelle lettre avant de l'insérer avant le noeud de référence
                          } else {
                            //sinon
                            cloneNode.appendChild(
                              document.createTextNode(
                                base.slice(i + value.length, exec["index"]),
                              ),
                            ); //on insère le texte depuis la dernière correspondance jusqu'à la nouvelle lettre à la fin du noeud
                          }
                          i = exec["index"]; //index de la dernière lettre trouvée
                        }
                        find = true; //il y a eu correspondance ça passe à true
                        const letterDys = document.createElement("strong"); //on crée le letter
                        letterDys.className = `${key?.replace("_", "-")}`; //on applique sa couleur
                        letterDys.dataset.LisioLetter = value.toLowerCase(); //on marque sa lettre
                        letterDys.innerText = exec[0]; //on insére la lettre trouvée dans le letter
                        if (
                          insertBefore != undefined &&
                          nodeCompl != undefined
                        ) {
                          //si on a un noeud de référence et que le neoud est complexe
                          cloneNode.insertBefore(letterDys, insertBefore); //on insère la lettre avant le noeud de référence
                        } else {
                          //sinon
                          cloneNode.appendChild(letterDys); //on l'insère en fin de texte
                        }
                      }
                      if (find) {
                        //si une correspondance a eu lieu
                        if (insertBefore != undefined && nodeCompl) {
                          //si on a un noeud de référence et que le neoud est complexe
                          cloneNode.insertBefore(
                            document.createTextNode(
                              base.slice(i + value.length),
                            ),
                            insertBefore,
                          ); //on insière la fin du texte avant le noeud de référence
                        } else {
                          //sinon
                          cloneNode.appendChild(
                            document.createTextNode(
                              base.slice(i + value.length),
                            ),
                          ); //on l'insère à la fin
                        }
                        index2 = 0; //on recommence au premier enfant pour les autres couleurs
                      }
                    }
                  }
                }
              }
            }
          }
          const parent = this._nodes[index].parentElement;
          if (parent != undefined) {
            parent.replaceChild(cloneNode, this._nodes[index]);
            if (this._nodes.indexOf(parent) != -1) {
              //si le noeud est un enfant d'un noeud présent dans le tableau
              this._nodes[this._nodes.indexOf(parent)].replaceChild(
                cloneNode,
                this._nodes[index],
              ); //on remplace l'ancien enfant par le nouveau
            }
          }
          this._nodes[index] = cloneNode; //on remplace l'ancien noeud par le nouveau dans le tableau
          // for(const tag of walker.tags.values()){
          //   if (tag.dataLisioIndex === cloneNode.dataLisioIndex) {
          //         this._tags[i] = cloneNode;
          //     }
          // }
        }
      }
    } else {
      LisioStyleSheetController.current.removeRule(".underline-red");
      LisioStyleSheetController.current.removeRule(".underline-green");
      LisioStyleSheetController.current.removeRule(".underline-blue");
    }
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  protected adaptFunction(element: HTMLElement, _value: boolean): void {
    if (
      element.textContent?.trim() !=
      "" /*&& element.className.baseVal == undefined*/
    ) {
      //on enlève les noeuds vides
      if (
        this._nodes.indexOf(element) == -1 &&
        element.localName != "i" &&
        element.localName != "script" &&
        element.localName != "style" &&
        !element.className.includes("icon")
      ) {
        //on vérifie si le texte est pas déjà stocké
        this._nodes.push(element);
        if (window.getComputedStyle(element).display == "flex") {
          element.style.display = "block";
        }
      }
    }
  }

  private deleteLetter(letter: HTMLElement) {
    const letterTxt = letter.innerText; //on récupère la lettre
    if (
      letter.previousSibling != undefined &&
      letter.previousSibling.nodeValue != undefined
    ) {
      //si il y a un noeud précédent
      if (letter.previousSibling.nodeType == 3) {
        //si ce noeud est un noeud texte
        letter.previousSibling.nodeValue =
          letter.previousSibling.nodeValue.concat(
            letterTxt,
            letter.nextSibling?.nodeValue == undefined
              ? ""
              : letter.nextSibling.nodeValue,
          ); //on rajoute la lettre à ce noeud
        if (letter.nextSibling != undefined) {
          letter.nextSibling.remove();
        }
        //letter.remove(); //on retire le letter
      }
    } else {
      //si pas de noeud avant
      if (letter.nextSibling != null) {
        if (letter.nextSibling.nodeType == 3) {
          //si ce noeud suivant est un noeud texte
          letter.nextSibling.nodeValue = letterTxt.concat(
            letter.nextSibling.nodeValue == undefined
              ? ""
              : letter.nextSibling.nodeValue,
          ); //on rajoute la lettre au noeud suivant
          letter.nextSibling.remove();
        }
      }
    }

    letter.remove(); //on retire le letter
  }
}

export default LisioDyslexiaAdapter;
