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

import LisioFontFamilyAdapter from "./adapters/fontStyleAdapter/lisio-font-family-adapter";
import LisioFontSizeAdapter from "./adapters/fontStyleAdapter/lisio-font-size-adapter";
import LisioLetterSpacingAdapter from "./adapters/fontStyleAdapter/lisio-letter-spacing-adapter";
import LisioLineHeightAdapter from "./adapters/fontStyleAdapter/lisio-line-height-adapter";
import LisioTextAlignAdapter from "./adapters/fontStyleAdapter/lisio-text-align-adapter";
import LisioWordSpacingAdapter from "./adapters/fontStyleAdapter/lisio-word-spacing-adapter";
import Adapter from "./adapters/lisio-adapter";
import LisioBiggerClickAdapter from "./adapters/lisio-bigger-click-adapter";
import LisioBookPageAdapter from "./adapters/lisio-book-page-adapter";
import LisioContrastAdapter from "./adapters/lisio-contrast-adapter";
import LisioCursorAdapter from "./adapters/lisio-cursor-adapter";
import LisioDaltonismAdapter from "./adapters/lisio-daltonism-adapter";
import LisioDictionaryAdapter from "./adapters/lisio-dictionary-adapter";
import LisioDyslexiaAdapter from "./adapters/lisio-dyslexia-adapter";
import LisioHightlightLinksAdapter from "./adapters/lisio-hightlight-links.adapter";
import LisioKeyboardNavigationAdapter from "./adapters/lisio-keyboard-navigation-adapter";
import LisioListAdapter from "./adapters/lisio-list-adapter";
import LisioReadingMask from "./adapters/lisio-reading-mask-adapter";
import LisioShowAltAdapter from "./adapters/lisio-show-alt-adapter";
import { LisioSpeechSynthesisAdapter } from "./adapters/lisio-speech-synthesis-adapter";
import LisioStopAnimationAdapter from "./adapters/lisio-stop-animation-adapter";
import LisioThemeAdapter from "./adapters/lisio-theme-adapter";
import LisioZoomAdapter from "./adapters/lisio-zoom-adapter";
import { LisioReaderAdapter } from "./adapters/reader/lisio-reader-adapter";
import LisioDeeplTranslationAdapter from "./adapters/translations/lisio-deepl-translation-adapter";
import LisioGoogleTranslationAdapter from "./adapters/translations/lisio-google-translation-adapter";
import { LisioBroadcastChannelController } from "./controllers/broadcastChannel/lisio-broadcast-channel-controller";
import { LisioBlurController } from "./controllers/lisio-blur-controller";
import { LisioMutationObserverController } from "./controllers/lisio-mutation-observer-controller";
import { LisioParamsController } from "./controllers/lisio-params-controller";
import LisioTranslationController from "./controllers/lisio-translation-controller";
import { LisioWidgetController } from "./controllers/lisio-widget-controller";
import LisioConfirmationModal from "./controllers/modals/lisio-confirmation-modal";
import LisioBroadcastChannelNames from "./enums/lisio-broadcast-channel-names";
import LisioMessageManager from "./managers/lisio-message-manager";
import { appendToHead, lisioGetInitLang } from "./utils";
import LisioTextTreeWalker from "./walkers/lisio-text-tree-walker";
import { LisioPopin } from "./popins/lisio-popin";
import { LisioConfig } from "./lisio-init";
import LisioLightMessageManager from "./managers/lisio-light-message-manager";
import LisioMessageReceivedName from "./enums/lisio-message-received-name";
import { LisioDisableBannerController, styles as disableStyles } from "./controllers/lisio-disable-banner-controller";
import LisioShadowRootController from "./controllers/lisio-shadow-root-controller";
import { GlobalContext } from "./global-context";

class Lisio {
  private _adapters: Map<
    LisioParameterNames,
    Adapter<string | number | boolean>
  > = new Map<LisioParameterNames, Adapter<string | number | boolean>>();
  private _broadcastChannel: LisioBroadcastChannelController;
  private _confirmationModal: LisioConfirmationModal;
  private _isBookPage = false;
  private _isReadingMode = false;
  private _lisioConfig: LisioConfig;
  private _loadingImage: HTMLImageElement;
  private _mutationObserverController: LisioMutationObserverController;

  // private _version: string = "";
  private _params: LisioParamsController = new LisioParamsController();
  private _popin?: LisioPopin;
  private _textTreeWalker: LisioTextTreeWalker;
  private _widget: LisioWidgetController;

  private _disableBand: LisioDisableBannerController;

  constructor(
    lisioConfig: LisioConfig,
    widget: LisioWidgetController,
    popin: LisioPopin | undefined,
  ) {
    this._broadcastChannel = new LisioBroadcastChannelController(
      LisioBroadcastChannelNames.READER,
    );
    this._disableBand = new LisioDisableBannerController("#ad1a00");
    this._lisioConfig = lisioConfig;
    this._widget = widget;
    if(widget.widget?.contentWindow != undefined){
      LisioMessageManager.current.contentWindow = widget.widget.contentWindow;
    }
    this._popin = popin;
    this._loadingImage = document.createElement("img");
    this._mutationObserverController = new LisioMutationObserverController(
      this._broadcastChannel,
      this,
    );
    this._textTreeWalker = new LisioTextTreeWalker(document.body, (n) => {
      if (n.nodeType === Node.TEXT_NODE) {
        if (
          n.nodeValue?.trim() != "" &&
          n.parentElement != undefined &&
          !["script", "noscript", "style", "title"].includes(
            n.parentElement.localName,
          )
        ) {
          return NodeFilter.FILTER_ACCEPT;
        } else {
          return NodeFilter.FILTER_REJECT;
        }
      }
      if (n.nodeType === Node.ELEMENT_NODE) {
        const el = n as HTMLElement;
        if (el.hasAttribute("placeholder")) {
          return NodeFilter.FILTER_ACCEPT;
        }
        return NodeFilter.FILTER_SKIP;
      }
      return NodeFilter.FILTER_SKIP;
    });

    this._isBookPage = window.location.origin.includes("mobiledition");
    if (__BUILD_TARGET__ === "extension") {
      this._isReadingMode = window.location.pathname === "/reading-mode.html";
      window.__LISIO_EXTENSION_ALREADY_INJECTED__ = true;
    } else {
      this._isReadingMode = window.location.origin === "null";
    }
    this._confirmationModal = new LisioConfirmationModal();
    this.initialize();
    if (__BUILD_TARGET__ === "extension" ? !this._isReadingMode : true) {
      if (document.readyState !== "loading") {
        LisioLightMessageManager.current.sendLisioFrontLoaded();
      } else {
        const loadedHandler = () => {
          LisioLightMessageManager.current.sendLisioFrontLoaded();
          document.removeEventListener("DOMContentLoaded", loadedHandler);
        };
        document.addEventListener("DOMContentLoaded", loadedHandler);
      }
    }
  }

  public get adapters() {
    return this._adapters;
  }

  public get confirmationModal() {
    return this._confirmationModal;
  }

  public get isBookPage() {
    return this._isBookPage;
  }

  public get isReadingMode() {
    return this._isReadingMode;
  }

  public get lisioConfig() {
    return this._lisioConfig;
  }

  public get loadingImage() {
    return this._loadingImage;
  }

  public get mutationObserverController() {
    return this._mutationObserverController;
  }

  public get params() {
    return this._params;
  }

  public get popin() {
    return this._popin;
  }

  public get textTreeWalker() {
    return this._textTreeWalker;
  }

  public get widget() {
    return this._widget;
  }

  public apply(fromObserver: boolean) {
    this.mutationObserverController.disconnectObserver();
    this._textTreeWalker.resetWalker();
    this._textTreeWalker.getTextes();
    for (const [paramName, value] of this.params.params.entries()) {
      if (
        (fromObserver &&
          paramName != LisioStringParameterNames.GOOGLE_TRANSLATION) ||
        !fromObserver
      ) {
        this.mutationObserverController.modifyPendingProcess(1);
        this._adapters.get(paramName)?.adapt(this._textTreeWalker, value);
        this.mutationObserverController.modifyPendingProcess(-1);
      }
    }
    this.mutationObserverController.connectObserver();
  }

  public applyFeature(
    feature: LisioParameterNames,
    value: string | boolean | number,
    defaultValue?: boolean | number | string,
  ) {
    if (this._mutationObserverController.observer == undefined) {
      this._mutationObserverController.setObserver();
    }
    this._mutationObserverController.disconnectObserver();
    const adpater = this._adapters.get(feature);
    if (
      booleanParameterNames.includes(feature as LisioBooleanParameterNames) &&
      typeof value === "boolean"
    ) {
      adpater?.adapt(this._textTreeWalker, value);
    } else if (
      stringParameterNames.includes(feature as LisioStringParameterNames) &&
      typeof value === "string"
    ) {
      adpater?.adapt(this._textTreeWalker, value);
    } else if (
      numberParameterNames.includes(feature as LisioNumericParameterNames) &&
      typeof value === "number"
    ) {
      if(([LisioNumericParameterNames.DALTONISM_R, LisioNumericParameterNames.DALTONISM_G, LisioNumericParameterNames.DALTONISM_B, LisioNumericParameterNames.DALTONISM_HUE, LisioNumericParameterNames.DALTONISM_SATURATION, LisioNumericParameterNames.DALTONISM_BRIGHTNESS] as unknown as LisioParameterNames).includes(feature)){
        adpater?.adapt(this._textTreeWalker, value, feature, defaultValue);
      } else {
        adpater?.adapt(this._textTreeWalker, value);
      }
    } else {
      console.log(`Type mismatch : ${feature}, ${value}`);
    }
    this._mutationObserverController.connectObserver();
  }

  public removeCharging() {
    this._loadingImage.remove();
  }

  public reset(removeAdaptation: boolean) {
    this._textTreeWalker.resetWalker();
    const speechSynthesisAdapter = this._adapters.get(
      LisioBooleanParameterNames.SPEECH_SYNTHESIS_STATE,
    ) as LisioSpeechSynthesisAdapter | undefined;
    if (speechSynthesisAdapter != undefined) {
      speechSynthesisAdapter.iText = speechSynthesisAdapter.lastIText;
    }

    const dyslexiaAdapter = this._adapters.get(
      LisioBooleanParameterNames.ACTIVE_UNDERLINE,
    ) as LisioDyslexiaAdapter | undefined;
    if (dyslexiaAdapter != undefined) {
      dyslexiaAdapter.nodes.splice(0, Infinity);
    }
    this._textTreeWalker.getTextes();
    if (removeAdaptation) {
      this.apply(false);
    }

    const readerAdapter = this._adapters.get(
      LisioBooleanParameterNames.READING_MODE,
    ) as LisioReaderAdapter | undefined;
    if (readerAdapter != undefined) {
      readerAdapter.all.splice(0, Infinity);
      readerAdapter.oldAll.splice(0, Infinity);
    }
  }

  public setLoading() {
    // this._loadingImage.src = `${import.meta.env.VITE_LISIO_DOMAIN}/solution/assets/load.gif`;
    // this._loadingImage.id = "lisio-loading";
    document.body.appendChild(this._loadingImage);
    document.body.style.cursor = "wait";
  }

  // private _popin: LisioPopin | undefined;

  /**
   * Permet de tout remettre par défaut
   */
  public setDefault(isConfigured: boolean, resetReadingMode: boolean) {
    if (this._isBookPage) {
      this._adapters
        .get(LisioBooleanParameterNames.BOOK_PAGE)
        ?.adapt(this._textTreeWalker, false);
    } else {
      for (const paramName of this._params.params.keys()) {
        let defaultValue: string | boolean | number | undefined;
        if (
          numberParameterNames.includes(paramName as LisioNumericParameterNames)
        ) {
          defaultValue = defautValueOfNumericParameters.get(
            paramName as LisioNumericParameterNames,
          );
        } else if (
          stringParameterNames.includes(paramName as LisioStringParameterNames)
        ) {
          defaultValue = "default";
        } else if (
          booleanParameterNames.includes(
            paramName as LisioBooleanParameterNames,
          )
        ) {
          defaultValue = false;
        }
        if (defaultValue != undefined) {
          const adapter = this._adapters.get(paramName);
          if(([LisioNumericParameterNames.DALTONISM_R, LisioNumericParameterNames.DALTONISM_G, LisioNumericParameterNames.DALTONISM_B, LisioNumericParameterNames.DALTONISM_HUE, LisioNumericParameterNames.DALTONISM_SATURATION, LisioNumericParameterNames.DALTONISM_BRIGHTNESS] as unknown as LisioParameterNames).includes(paramName)){
            adapter?.adapt(this._textTreeWalker, defaultValue, paramName, defaultValue);
          } else {
            adapter?.adapt(this._textTreeWalker, defaultValue);
          }
        }
      }
      this._params.reset();
      this._textTreeWalker.resetWalker();

      this._mutationObserverController.disconnectObserver();

      if (resetReadingMode) {
        if (
          window.location.origin != "null" ||
          window.location.pathname != "/reading-mode.html"
        ) {
          window.localStorage.removeItem("lisio_reading_mode");
          this._broadcastChannel?.sendCloseMessage();
        } else {
          window.close();
        }
      }
      LisioTranslationController.current.loadTranslationFile(
        lisioGetInitLang(),
      );
      this._popin?.refreshLabel(false, isConfigured);
      this.setDisableBand(false);
    }
  }

  public newTab(url: string, ecoOrRural: boolean, queryUrl: string) {
    url = decodeURIComponent(decodeURIComponent(url));
    if (url == "default") {
      fetch(import.meta.env.VITE_PHP_SERVER, {
        mode: "no-cors",
      })
        .then(() => {
          const newUrl = window.location.href.replace(
            /&lisioUsernames%3D.+&lisioUsers%3D[\w-%3D]*/,
            "",
          );
          window.location.assign(
            `${import.meta.env.VITE_PHP_SERVER}/IM_GenerationMinisite.php?wsu=lisiojs${import.meta.env.VITE_TEST_MODE}${
              ecoOrRural ? "&mode=eco" : ""
            }&fic_id=${window.lisioAccesskey.trim()}&${queryUrl}&url=${
              newUrl.charAt(newUrl.length - 1) === "?"
                ? window.location.origin + window.location.pathname
                : newUrl
            }`,
          );
        })
        .catch((error) => {
          console.log(error);
          //alert("Désolé, le format page de livre n'est pas disponible pour le moment !");
        });
    } else {
      if (window.location.href.includes("IM_GenerationMinisite.php")) {
        //message manager add handler
        const handler = (datas: string) => {
          window.location.assign(`${url}?&${datas}`);
        };
        LisioMessageManager.current.attachHandler(
          LisioMessageReceivedName.ALL_ENCODED_RESPONSE,
          handler,
        );
        LisioMessageManager.current.sendAllEncodedMessage();
      } else {
        window.location.assign(url);
      }
    }
  }

  private initialize(): void {
    this._adapters.set(
      LisioNumericParameterNames.FONT_SIZE,
      new LisioFontSizeAdapter(),
    );
    this._adapters.set(
      LisioNumericParameterNames.LINE_HEIGHT,
      new LisioLineHeightAdapter(),
    );
    this._adapters.set(
      LisioNumericParameterNames.WORD_SPACING,
      new LisioWordSpacingAdapter(),
    );
    this._adapters.set(
      LisioNumericParameterNames.LETTER_SPACING,
      new LisioLetterSpacingAdapter(),
    );
    this._adapters.set(
      LisioNumericParameterNames.CONTRAST,
      new LisioContrastAdapter(),
    );
    if (!this._lisioConfig.userSettings.tabsToHide.includes("sur_zom")) {
      this._adapters.set(
        LisioNumericParameterNames.ZOOM,
        new LisioZoomAdapter(),
      );
    }
    this._adapters.set(
      LisioNumericParameterNames.BIGGER_CLICK,
      new LisioBiggerClickAdapter(),
    );
    this._adapters.set(
      LisioBooleanParameterNames.DISABLE_ANIMATION,
      new LisioStopAnimationAdapter(),
    );
    this._adapters.set(
      LisioBooleanParameterNames.HIGHLIGHT,
      new LisioHightlightLinksAdapter(),
    );
    this._adapters.set(
      LisioBooleanParameterNames.SHOW_ALT,
      new LisioShowAltAdapter(),
    );

    const deeplAdapter = new LisioDeeplTranslationAdapter(
      this._mutationObserverController,
      this._params,
    );
    this._adapters.set(
      LisioStringParameterNames.DEEPL_TRANSLATION,
      deeplAdapter,
    );

    const listAdapter = new LisioListAdapter(
      this.lisioConfig,
      this._params,
      deeplAdapter,
    );
    this._adapters.set(LisioBooleanParameterNames.LIST, listAdapter);
    deeplAdapter.listAdapter = listAdapter;

    this._adapters.set(
      LisioBooleanParameterNames.READING_MASK,
      new LisioReadingMask(),
    );
    this._adapters.set(
      LisioStringParameterNames.THEME,
      new LisioThemeAdapter(),
    );
    this._adapters.set(
      LisioStringParameterNames.FONT_FAMILY,
      new LisioFontFamilyAdapter(),
    );
    this._adapters.set(
      LisioBooleanParameterNames.KEYBOARD_NAVIGATION,
      new LisioKeyboardNavigationAdapter(),
    );
    this._adapters.set(
      LisioStringParameterNames.TEXT_ALIGN,
      new LisioTextAlignAdapter(),
    );
    this._adapters.set(
      LisioStringParameterNames.CURSOR_SIZE,
      new LisioCursorAdapter(),
    );
    const daltonismeAdapter = new LisioDaltonismAdapter();
    this._adapters.set(
      LisioStringParameterNames.DALTONISM,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_R,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_G,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_B,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_HUE,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_SATURATION,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioNumericParameterNames.DALTONISM_BRIGHTNESS,
      daltonismeAdapter,
    );
    this._adapters.set(
      LisioBooleanParameterNames.VOCA,
      new LisioDictionaryAdapter(),
    );
    if ("speechSynthesis" in window) {
      this._adapters.set(
        LisioBooleanParameterNames.SPEECH_SYNTHESIS_STATE,
        new LisioSpeechSynthesisAdapter(
          this._textTreeWalker,
          this._mutationObserverController,
          this._params,
          this._lisioConfig.userSettings.vocalSynthesisMode,
        ),
      );
    }

    const dyslexia = new LisioDyslexiaAdapter(this._params);
    this._adapters.set(LisioBooleanParameterNames.ACTIVE_UNDERLINE, dyslexia);
    this._adapters.set(
      LisioStringParameterNames.GOOGLE_TRANSLATION,
      new LisioGoogleTranslationAdapter(),
    );

    if (!this.isReadingMode) {
      // this._adapters.set(
      //   LisioBooleanParameterNames.READING_MODE,
      //   new LisioReaderAdapter(this._broadcastChannel, this._lisioConfig),
      // );
      this._adapters.set(
        LisioBooleanParameterNames.BOOK_PAGE,
        new LisioBookPageAdapter(this._isBookPage, this),
      );
    }
    this._adapters.set(LisioStringParameterNames.UNDERLINE_RED, dyslexia);
    this._adapters.set(LisioStringParameterNames.UNDERLINE_GREEN, dyslexia);
    this._adapters.set(LisioStringParameterNames.UNDERLINE_BLUE, dyslexia);

    if (this._lisioConfig.userSettings.vocalSynthesisMode === 1) {
      document.documentElement.classList.add("lisio-vocal-synthesis-mode-1");
    }
    const style = document.createElement("style");
    style.id = "lisio-common-styles";
    appendToHead(style);
    window.addEventListener("visibilitychange", () => {
      if (document.visibilityState === "hidden") {
        window.speechSynthesis?.cancel(); //stop la synthèse vocale lorsqu'on quitte la page
      }
    });

    LisioMessageManager.current.initMessages(this);

    LisioBlurController.current.blur.addEventListener("click", () => {
      if (!this._confirmationModal.isEnabled) {
        this._widget.closeWidget();
      }
    });
  }

  public setDisableBand(adaptation: boolean){
    this._mutationObserverController.disconnectObserver();
    if (adaptation) {
      if (!GlobalContext.current.isMobile) {
        LisioShadowRootController.current.appendElement(
          this._disableBand.banner,
          disableStyles,
        );
        document.body.appendChild(this._disableBand.spacer);
      }
      this._disableBand?.showBanner();
    } else {
      this._disableBand?.hideBanner();
    }
    this._mutationObserverController.connectObserver();
  }
}

const booleanParameterNames = Object.values(LisioBooleanParameterNames);
const stringParameterNames = Object.values(LisioStringParameterNames);
const numberParameterNames = Object.values(LisioNumericParameterNames);

export { Lisio };