/**
 * @mergeTarget
 * @module Src/Screens/Translation
 */

import {
  LisioProfileNames,
  LisioParameterNames,
  LisioStringParameterNames,
  LisioCategory,
  LisioUser,
  DeeplTranslationOptions,
  GoogleTranslationOptions,
  LisioCategoryNames,
} from "@lisio/lisio-profils";
import { ElementsContainer } from "../../components/containers/elements-container/elements-container";
import { LisioScreen } from "../lisio-screen";
import {
  CategoryRenderObject,
  ProfileRenderObject,
  BooleanParameterRenderObject,
  StringParameterRenderObject,
  NumberParameterRenderObject,
  ComponentAndPosition,
  VisualCategoryNames,
  ScreenNames,
  ISOToIcon,
} from "../screen-types";
import { HomeScreen } from "../home/home-screen";
import { LisioComponent } from "@lisio/lisio-engine";
import { MoreScreen } from "../more/more-screen";
import { ListContainer } from "../../components/containers/list-container/list-container";
import { CustomText } from "../../components/custom-text/custom-text";
import {
  TranslationController,
  TranslationLanguages,
} from "../../../controllers/translation/translation-controller";
import { translateEvent, voiceEvent } from "../../../../misc/events";
import { getDirection } from "../../../controllers/translation/translation-languages-enum-and-types";

/**
 * Class representing a transaltion screen component extending LisioScreen.\
 * It aims to represent a transaltion screen component.\
 * A transaltion screen component is basically a component to render all transaltion categories, profiles and parameters.\
 */
class TranslationScreen extends LisioScreen {
  /**
   * Protected attribute to store category render objects
   * @source
   */
  protected _categoryRenderObjects: Map<
    VisualCategoryNames,
    CategoryRenderObject
  > = new Map<VisualCategoryNames, CategoryRenderObject>([
    [
      VisualCategoryNames.MORE,
      {
        buttonId: `${this.id}-more`,
        icon: {
          fileName: "footer-icons",
          iconName: "More",
        },
        titleId: "more-button",
        nextScreenName: MoreScreen.name,
        position: 10,
        isActive: false,
        activeSubObjects: new Set(),
        iconsClasses: ["grey2"],
        cssClasses: ["secondary", "footer-button"],
      },
    ],
  ]);

  /**
   * Protected attribute to store profile render objects
   * @source
   */
  protected _profileRenderObjects: Map<LisioProfileNames, ProfileRenderObject> =
    new Map<LisioProfileNames, ProfileRenderObject>();

  /**
   * Protected attribute to store parameter render objects
   * @source
   */
  protected _parameterRenderObjects: Map<
    LisioParameterNames,
    | BooleanParameterRenderObject
    | StringParameterRenderObject
    | NumberParameterRenderObject
  > = new Map<
    LisioParameterNames,
    | BooleanParameterRenderObject
    | StringParameterRenderObject
    | NumberParameterRenderObject
  >([
    [
      LisioStringParameterNames.GOOGLE_TRANSLATION,
      {
        type: "string",
        name: LisioStringParameterNames.GOOGLE_TRANSLATION,
        items: Object.values(GoogleTranslationOptions).map(
          (googleTranslationValue) => {
            return {
              textId: `translation-${googleTranslationValue
                .toLowerCase()
                .replace(/_(\w+)/, "")}`,
              value: googleTranslationValue,
              isChecked: false,
              icon: {
                fileName: "trans-icons",
                iconName:
                  ISOToIcon[
                    googleTranslationValue.toUpperCase() as keyof typeof ISOToIcon
                  ],
              },
              iconClasses: ["no-ratio", "border-flags", "shadow-flags"],
              specificLabel: ["eu", "br", "co", "ca", "oc"].includes(
                googleTranslationValue,
              )
                ? {
                    textId: "specific-label-regional",
                  }
                : undefined,
            };
          },
        ),
        defaultText: "translation-default-text",
        position: 0,
        cssClasses: ["secondary", "full"],
        cssClassesItems: ["content-row"],
        cssClassesLabelItems: ["row", "text-tall"],
      } as StringParameterRenderObject,
    ],
    [
      LisioStringParameterNames.DEEPL_TRANSLATION,
      {
        type: "string",
        name: LisioStringParameterNames.DEEPL_TRANSLATION,
        items: Object.values(DeeplTranslationOptions).map(
          (deeplTranslationValue) => {
            return {
              textId: `translation-${deeplTranslationValue
                .toLowerCase()
                .replace(/_/, "-")}`,
              value: deeplTranslationValue,
              isChecked: false,
              icon: {
                fileName: "trans-icons",
                iconName:
                  ISOToIcon[
                    deeplTranslationValue.toUpperCase() as keyof typeof ISOToIcon
                  ],
              },
              iconClasses: ["no-ratio", "border-flags", "shadow-flags"],
            };
          },
        ),
        position: 0,
        cssClasses: ["secondary", "full"],
        cssClassesItems: ["content-row"],
        cssClassesLabelItems: ["align-items-center", "text-tall"],
      } as StringParameterRenderObject,
    ],
  ]);

  /**
   * Private attribute to store is deepl translation
   * @source
   */
  private _isDeepl: Boolean = false;

  /**
   * Getter for attribute {@link _isDeepl | _isDeepl}
   * @returns Returns _isDeepl attribute
   * @source
   */
  public get isDeepl(): Boolean {
    return this._isDeepl;
  }

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a transaltion screen in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
    <section id="${this._id}" role="tabpanel">
      <children></children>
    </section>
    `;
  }

  private sortingLanguages(
    priorizedLanguages: string[],
    aIso: string,
    bIso: string,
    aName: string,
    bName: string,
  ): number {
    const indexA = priorizedLanguages.indexOf(aIso);
    const indexB = priorizedLanguages.indexOf(bIso);

    if (indexA !== -1 && indexB !== -1) {
      return indexA - indexB;
    }

    if (indexA !== -1) return -1;
    if (indexB !== -1) return 1;

    return aName.localeCompare(bName);
  }

  private getFormattedIsoInstalled(voices: SpeechSynthesisVoice[]) {
    return [
      ...new Set(
        voices.map((voice) => voice.lang.toUpperCase().replace("-", "_")),
      ),
    ];
  }

  private voiceInstalled() {
    return new Promise((res, _rej) => {
      const voices = window.speechSynthesis.getVoices();
      if (voices.length === 0) {
        window.speechSynthesis.onvoiceschanged = () => {
          const voices = window.speechSynthesis.getVoices();
          res(this.getFormattedIsoInstalled(voices));
        };
      } else {
        res(this.getFormattedIsoInstalled(voices));
      }
    });
  }

  /**
   * Constructor of class {@link TranslationScreen | TranslationScreen}
   * @param {LisioCategory} translation - Translation category
   * @param {string[]} priorizedLanguages - Languages to priorize in list
   * @param {string[]} authorizedLanguagesISOs - Authorized languages
   * @param {boolean} isCompensation - Compensation banner has to be displayed
   * @param {LisioUser | undefined} user - Current user
   * @source
   */
  constructor(
    translation: LisioCategory,
    priorizedLanguages: string[],
    authorizedLanguagesISOs: string[],
    isCompensation?: boolean,
    user?: LisioUser,
  ) {
    ScreenNames[TranslationScreen.name] = "translation-screen";
    super(
      TranslationScreen.name,
      TranslationScreen.name,
      translation.name,
      HomeScreen.name,
    );
    const promise = this.voiceInstalled();
    const parametersToHide = [];

    const parameters = this._parameterRenderObjects.get(
      translation.name === LisioCategoryNames.DEEPL_TRANSLATION
        ? LisioStringParameterNames.DEEPL_TRANSLATION
        : LisioStringParameterNames.GOOGLE_TRANSLATION,
    ) as StringParameterRenderObject;
    if (translation.name === LisioCategoryNames.DEEPL_TRANSLATION) {
      this._isDeepl = true;
      parameters.items = parameters.items.filter((item) =>
        authorizedLanguagesISOs.includes(item.value),
      );
      parametersToHide.push(LisioStringParameterNames.GOOGLE_TRANSLATION);
    } else {
      parametersToHide.push(LisioStringParameterNames.DEEPL_TRANSLATION);
    }
    parameters.items.sort((a, b) =>
      this.sortingLanguages(
        priorizedLanguages,
        a.value.toLowerCase().replace("_", "-"),
        b.value.toLowerCase().replace("_", "-"),
        TranslationController.current.getTranslation(a.textId),
        TranslationController.current.getTranslation(b.textId),
      ),
    );

    const parametersElements: ComponentAndPosition[] = this.initParameters(
      translation.parameterNames,
      parametersToHide,
      user?.customParameters,
    );

    parametersElements.sort((a, b) => a.position - b.position);
    const infobox =
      translation.name === LisioCategoryNames.DEEPL_TRANSLATION
        ? "infobox-safe-trad"
        : "infobox-trad";
    const elementsTitleContainer: LisioComponent = this.createScreenTitle(
      "translation-title",
      "h2",
      infobox,
    );

    const elementsContainer: LisioComponent = new ElementsContainer(
      parametersElements.map((group) => group.component),
      undefined,
      ["full", "column", "secondary"],
    );

    const textSection: LisioComponent = new ElementsContainer(
      [new CustomText("p", infobox, false, ["info-text"], undefined, infobox)],
      undefined,
      ["column", "full", "secondary", "infobox"],
    );
    const contentAndFooterContainer: LisioComponent = new ListContainer(
      [
        textSection,
        elementsContainer,
        ...(isCompensation ? this.createFooter(isCompensation) : []),
      ],
      undefined,
      ["column test", "screen-ul", "width-100"],
    );
    this._children?.push(elementsTitleContainer, contentAndFooterContainer);
    window.addEventListener(translateEvent.type, (e: any) => {
      const lang =
        e.detail.flagIso === TranslationLanguages.DEFAULT
          ? TranslationController.current.defaultLang
          : e.detail.flagIso;
      document.querySelector("html")?.setAttribute("dir", getDirection(lang));
      document
        .querySelector("html")
        ?.setAttribute("lang", lang.replace("_", "-"));
      document.title =
        TranslationController.current.getTranslation("iframeTitle");
    });
    promise.then((voicesIso) => {
      window.dispatchEvent(
        new CustomEvent(voiceEvent.type, {
          detail: {
            voices: voicesIso,
          },
        }),
      );
    });
  }
}

export { TranslationScreen };
