import { FontFamilyOptions } from "@lisio/lisio-profils";

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";
import { appendToHead } from "../../utils";

/**
 * Class representing a font-family adapter extending an adapter.\
 * It aims to represents the font style functionality of Lisio.\
 * A font-family adapter is basically a functionality of Lisio which will change the font-family of texts in the main page.\
 */
class LisioFontFamilyAdapter extends LisioAdapter<string> {
  private _correspondingFonts: Map<string, string> = new Map<string, string>([
    ["default", "default"],
    [FontFamilyOptions.ANDIKA, "Andika"],
    [FontFamilyOptions.ARIAL, "Arial"],
    [FontFamilyOptions.COMIC_NEUE, "Comic Neue"],
    [FontFamilyOptions.INCLUSIVE_SANS, "Inclusive Sans"],
    [FontFamilyOptions.LUCIOLE, "Luciole"],
    [FontFamilyOptions.OPEN_DYSLEXIC, "Open Dyslexic"],
  ]);

  private _fontFiles: Map<string, string> = new Map<string, string>([
    [FontFamilyOptions.ANDIKA, "andika"],
    [FontFamilyOptions.COMIC_NEUE, "comic-neue"],
    [FontFamilyOptions.INCLUSIVE_SANS, "inclusive-sans"],
    [FontFamilyOptions.LUCIOLE, "luciole"],
    [FontFamilyOptions.OPEN_DYSLEXIC, "open-dyslexic"],
  ]);

  /**
   * Public method implementing abstract method adapt of Adapter
   * @param {LisioTextTreeWalker} walker - A walker to explore the DOM
   * @param {string} value - Value of the functionality
   * @returns Returns nothing
   * @source
   */
  public adapt(walker: LisioTextTreeWalker, value: string): void {
    const convertedValue: string | undefined =
      this._correspondingFonts.get(value);
    if (convertedValue != undefined) {
      const fontFile = this._fontFiles.get(value);
      if (fontFile != undefined) {
        if (__BUILD_TARGET__ === "extension") {
          const fontUrl = chrome.runtime.getURL(`css/${fontFile}.css`);
          fetch(fontUrl)
            .then((res) => res.text())
            .then((text) => {
              const rules = text.split("}");
              for (let rule of rules) {
                const matches = rule.match(/url\(["']?([^\s"')]+)["']?\)/gm);
                if (matches != undefined) {
                  for (const match of matches) {
                    const fileMatch = match.match(
                      /url\(["']?([^\s"')]+)["']?\)/,
                    );
                    if (fileMatch != undefined) {
                      rule = rule.replace(
                        match,
                        `url(${chrome.runtime.getURL(fileMatch[1])})`,
                      );
                    }
                  }
                  const weight = rule.match(/font-weight:\s?(\w+)/);
                  const style = rule.match(/font-style:\s?(\w+)/);
                  if (weight != undefined && style != undefined) {
                    LisioStyleSheetController.current.insertRawRule(
                      `${convertedValue}-${weight[0]}-${style[0]}`,
                      `${rule}}`,
                    );
                  }
                }
              }
            });
        } else {
          if (
            document.querySelector(`link[href*="${fontFile}.css"]`) == undefined
          ) {
            const link = document.createElement("link");
            link.rel = "stylesheet";
            link.href =
              __BUILD_TARGET__ === "site"
                ? `${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/css/${fontFile}.css`
                : chrome.runtime.getURL(`/dist/css/${fontFile}.css`);
            appendToHead(link);
          }
        }
      }

      for (const tag of walker.tags.values()) {
        if (tag.localName != "option") {
          this.adaptFunction(tag, convertedValue);
        }
      }

      // const formTags = document.body.querySelectorAll('input, textarea, i'); // on récupère tout ce qui ne compte pas comme des noeuds textes mais qui peuvent contenir du texte
      const formTags: NodeListOf<HTMLElement> = document.body.querySelectorAll(
        "input, select, textarea, optgroup, i",
      );
      for (const formTag of formTags) {
        this.adaptFunction(formTag, convertedValue);
      }

      LisioStyleSheetController.current.removeRule(".lisio-fonts");
      LisioStyleSheetController.current.removeRule(".lisio-fonts *");
      if (convertedValue !== "default") {
        LisioStyleSheetController.current.insertRule(
          ".lisio-fonts",
          new Map([[LisioCSSProperties.FONT_FAMILY, convertedValue]]),
        );
        LisioStyleSheetController.current.insertRule(
          ".lisio-fonts *",
          new Map([[LisioCSSProperties.FONT_FAMILY, convertedValue]]),
        );
      }
    }
  }

  protected adaptFunction(element: HTMLElement, value: string): void {
    if (typeof element.className === "string") {
      if (
        value != "default" &&
        element.tagName != "I" &&
        !element.className.includes("material-icons") &&
        !element.className.includes("material-symbols") &&
        !element.className.includes("glyphicon") &&
        !element.className.includes("fa-") &&
        !element.className.includes("AEfont-") &&
        !element.className.includes("icon")
      ) {
        element.classList.add(`lisio-fonts`); //on ajoute une class
        LisioStyleSheetController.current.replaceImportantInStyleAttribute(
          [LisioCSSProperties.FONT_FAMILY],
          element,
        );
        if (
          value == FontFamilyOptions.OPEN_DYSLEXIC &&
          !element.getAttribute("lisio-already-min") &&
          element.localName != "strong"
        ) {
          const fontSize: number = parseInt(
            window.getComputedStyle(element).fontSize,
          );
          if (fontSize > 20) {
            const size: number = Math.floor(fontSize * 0.75);
            LisioStyleSheetController.current.insertRule(
              `.lisio-font-size-dys-${size}`,
              new Map<LisioCSSProperties, string>([
                [LisioCSSProperties.FONT_SIZE, `${size}px`],
              ]),
            );
            element.classList.add(`lisio-font-size-dys-${size}`);
            element.setAttribute("lisio-already-min", "true");
          }
        } else if (
          value != FontFamilyOptions.OPEN_DYSLEXIC &&
          element.getAttribute("lisio-already-min") &&
          element.localName != "strong"
        ) {
          const classToRemove = Array.from(element.classList).find(
            (className) => className.startsWith("lisio-font-size-dys-"),
          );
          if (classToRemove) {
            element.classList.remove(classToRemove);
          }
          element.removeAttribute("lisio-already-min");
        }
      } else {
        element.classList.remove(`lisio-fonts`);
        const classToRemove = Array.from(element.classList).find((className) =>
          className.startsWith("lisio-font-size-dys-"),
        );
        if (classToRemove) {
          element.classList.remove(classToRemove);
        }
        element.removeAttribute("lisio-already-min");
      }
    }
  }
}

export default LisioFontFamilyAdapter;
