/**
 * @mergeTarget
 * @module Src/Components/CustomNav
 */

import { AccessibilityScreen } from "../../screens/accessibility/accessibility-screen";
import { ComfortScreen } from "../../screens/comfort/comfort-screen";
import { EcoScreen } from "../../screens/eco/eco-screen";
import { SettingsScreen } from "../../screens/settings/settings-screen";
import { SynthScreen } from "../../screens/synth/synth-screen";
import { TranslationScreen } from "../../screens/translation/translation-screen";
import { NavButtonSubRenderObject } from "../component-types";
import { ListContainer } from "../containers/list-container/list-container";
import { CustomIcon } from "../custom-icon/custom-icon";
import { customNavStyles } from "./custom-nav.css";
import { RuralScreen } from "../../screens/rural/rural-screen";
import { LisioComponent, LisioComponentStyles } from "@lisio/lisio-engine";
import { HomeScreen } from "../../screens/home/home-screen";
import { CustomButton } from "../custom-button/custom-button";
import { ScreenManager } from "../../../managers/screen-manager";
import { ScreenNames } from "../../screens/screen-types";
import { LisioCategoryNames } from "@lisio/lisio-profils";

/**
 * ## How to add a new screen in navigation ?
 *
 * First, create corresponding render object in {@link CustomNav._subRenderObjects | _subRenderObjects}
 * That's all, if you aslo add your screen in your widget version of course
 *
 * ## How to modify a screen in navigation ?
 *
 * Modify corresponding render object in {@link CustomNav._subRenderObjects | _subRenderObjects}
 *
 * ## How to delete a screen in navigation ?
 *
 * First, delete corresponding render object in {@link CustomNav._subRenderObjects | _subRenderObjects}
 * That's all, if you aslo delete your screen in your widget version of course
 *
 * ## Documentation
 *
 * Class representing a navigation component extending LisioComponent.\
 * It aims to represent a navigation component.\
 * A navigation component is basically a navigation menu.\
 */
class CustomNav extends LisioComponent {
  /**
   * Private attribute to store all navigation buttons
   * @source
   */
  private _navButtons: string[] = [];

  /**
   * Private attribute to store selected button
   * @source
   */
  private _selectedNavButton?: CustomButton;

  /**
   * Private attribute to store previous sibling of selected element
   * @source
   */
  private _previousSiblingSelectedNavButton?: CustomButton;

  /**
   * Private attribute to store next sibling of selected element
   * @source
   */
  private _nextSiblingSelectedNavButton?: CustomButton;

  /**
   * Private attribute to store all possible navigation buttons render objects
   * @source
   */
  private _subRenderObjects: Map<string, NavButtonSubRenderObject> = new Map<
    string,
    NavButtonSubRenderObject
  >([
    [
      ScreenNames[AccessibilityScreen.name],
      {
        buttonId: `${ScreenNames[AccessibilityScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Human",
          cssClasses: ["grey"],
        },
        nextScreenName: AccessibilityScreen.name,
        position: 2,
        aria: {
          label: {
            id: "accessibility-title",
            noTranslate: false,
          },
          controls: ScreenNames[AccessibilityScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[ComfortScreen.name],
      {
        buttonId: `${ScreenNames[ComfortScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Computer",
          cssClasses: ["grey"],
        },
        nextScreenName: ComfortScreen.name,
        position: 3,
        aria: {
          label: {
            id: "comfort-title",
            noTranslate: false,
          },
          controls: ScreenNames[ComfortScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[RuralScreen.name],
      {
        buttonId: `${ScreenNames[RuralScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Network",
          cssClasses: ["grey"],
        },
        nextScreenName: RuralScreen.name,
        position: 4,
        aria: {
          label: {
            id: "rural-title",
            noTranslate: false,
          },
          controls: ScreenNames[RuralScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[TranslationScreen.name],
      {
        buttonId: `${ScreenNames[TranslationScreen.name]}-nav`,
        icon: {
          fileName: "trans-icons",
          iconName: "fr",
          cssClasses: ["grey", "trans-icon"],
        },
        nextScreenName: TranslationScreen.name,
        position: 1,
        aria: {
          label: {
            id: "translation-title",
            noTranslate: false,
          },
          controls: ScreenNames[TranslationScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[SynthScreen.name],
      {
        buttonId: `${ScreenNames[SynthScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Speak",
          cssClasses: ["grey", "speak-icon"],
        },
        nextScreenName: SynthScreen.name,
        position: 5,
        aria: {
          label: {
            id: "synth-title",
            noTranslate: false,
          },
          controls: ScreenNames[SynthScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[EcoScreen.name],
      {
        buttonId: `${ScreenNames[EcoScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Leaf",
          cssClasses: ["grey", "leaf"],
        },
        nextScreenName: EcoScreen.name,
        position: 6,
        aria: {
          label: {
            id: "eco-title",
            noTranslate: false,
          },
          controls: ScreenNames[EcoScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
    [
      ScreenNames[SettingsScreen.name],
      {
        buttonId: `${ScreenNames[SettingsScreen.name]}-nav`,
        icon: {
          fileName: "misc-icons",
          iconName: "Settings",
          cssClasses: ["grey"],
        },
        nextScreenName: SettingsScreen.name,
        position: 7,
        aria: {
          label: {
            id: "settings-title",
            noTranslate: false,
          },
          controls: ScreenNames[SettingsScreen.name],
        },
        role: "tab",
        tabindex: "-1",
        cssClasses: ["secondary"],
      },
    ],
  ]);

  public get navButtons() {
    return this._navButtons;
  }

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a navigation in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
      <div class="text-nav-button-list hidden" aria-hidden="true" role="tablist">
        <children></children>
      </div>
        `;
  }

  /**
   * Public method to select tab and change screen
   * @param {string} nextScreenName - Next screen to go to
   * @returns Returns nothing
   * @source
   */
  public selectTab(nextScreenName: string): void {
    const nextScreenTabIndex: number = this._navButtons.indexOf(nextScreenName);
    if (nextScreenTabIndex != -1) {
      const tabs: LisioComponent[] = this._children[0].children;
      if (this._selectedNavButton != undefined) {
        this._selectedNavButton.htmlElement
          ?.closest("li")
          ?.classList.remove("selected");
        this._selectedNavButton.htmlElement?.classList.remove("selected");
        this._selectedNavButton.htmlElement?.setAttribute(
          "aria-selected",
          "false",
        );
        this._selectedNavButton.htmlElement?.setAttribute("tabindex", "-1");
      }
      this._selectedNavButton = tabs[nextScreenTabIndex]
        .children[0] as CustomButton;
      this._selectedNavButton.htmlElement
        ?.closest("li")
        ?.classList.add("selected");
      this._selectedNavButton.htmlElement?.classList.add("selected");
      this._selectedNavButton.htmlElement?.setAttribute(
        "aria-selected",
        "true",
      );
      this._selectedNavButton.htmlElement?.setAttribute("tabindex", "0");

      if (this._previousSiblingSelectedNavButton != undefined) {
        this._previousSiblingSelectedNavButton.htmlElement
          ?.closest("li")
          ?.classList.remove("previous");
        this._previousSiblingSelectedNavButton.htmlElement?.classList.remove(
          "previous",
        );
      }
      if (nextScreenTabIndex - 1 >= 0) {
        this._previousSiblingSelectedNavButton = tabs[nextScreenTabIndex - 1]
          .children[0] as CustomButton;
        this._previousSiblingSelectedNavButton.htmlElement
          ?.closest("li")
          ?.classList.add("previous");
        this._previousSiblingSelectedNavButton.htmlElement?.classList.add(
          "previous",
        );
      }

      if (this._nextSiblingSelectedNavButton != undefined) {
        this._nextSiblingSelectedNavButton.htmlElement?.classList.remove(
          "next",
        );
      }
      if (nextScreenTabIndex + 1 < tabs.length) {
        this._nextSiblingSelectedNavButton = tabs[nextScreenTabIndex + 1]
          .children[0] as CustomButton;
        this._nextSiblingSelectedNavButton.htmlElement?.classList.add("next");
      }
    } else {
      const tabs: LisioComponent[] = this._children[0].children;
      this._htmlElement?.querySelectorAll("button").forEach((button) => {
        button.closest("li")!.classList.remove("selected");
        button.closest("li")!.classList.remove("previous");
        button.classList.remove("selected");
        button.classList.remove("previous");
        button.classList.remove("next");
        button.setAttribute("aria-selected", "false");
        button.setAttribute("tabindex", "-1");
      });
      this._htmlElement
        ?.querySelectorAll("button")[0]
        .setAttribute("tabindex", "0");
      this._selectedNavButton = tabs[0].children[0] as CustomButton;
    }
  }

  /**
   * Public method to toggle nav when needed
   * @param {boolean} isShown - Indicates if navigation has to be shown
   * @returns Returns nothing
   * @source
   */
  public toggleNav(isShown: boolean): void {
    if (isShown) {
      this._htmlElement?.classList.remove("hidden");
      this._htmlElement?.removeAttribute("aria-hidden");
    } else {
      this._htmlElement?.classList.add("hidden");
      this._htmlElement?.setAttribute("aria-hidden", "true");
    }
  }

  /**
   * Public method to add breadcrumb in nav
   * @param {boolean} isCategoryRenderObjectEmpty - Indicates if category has sub element enabled or not
   * @param {string} currentScreenName - Current scren name in screen tree
   * @param {string} parentScreenName - Parent screen name in screen tree
   * @returns Returns nothing
   * @source
   */
  public breadcrumb(
    isCategoryRenderObjectEmpty: boolean,
    currentScreenName: string,
    parentScreenName: string,
  ): void {
    const indexOfScreenNavButton: number = this._navButtons.indexOf(
      parentScreenName === ScreenNames[HomeScreen.name]
        ? currentScreenName
        : parentScreenName,
    );
    if (indexOfScreenNavButton >= 0) {
      if (isCategoryRenderObjectEmpty) {
        this._children[0].children[
          indexOfScreenNavButton
        ]?.htmlElement?.classList.remove("is-active");
      } else {
        if (
          this._children[0].children[indexOfScreenNavButton].htmlElement ==
          undefined
        ) {
          this._children[0].children[indexOfScreenNavButton]?.addClasses([
            "is-active",
          ]);
        } else {
          this._children[0].children[
            indexOfScreenNavButton
          ]?.htmlElement?.classList.add("is-active");
        }
      }
    }
  }

  /**
   * Constructor of class {@link CustomNav | CustomNav}
   * @param {string} screenNames - All screen name to include in navigation
   * @param {boolean} isFlag - Is flag icon
   * @param {string | undefined} ecoMode - Full or light translation to adapt the icon
   * @param {string[] | undefined} cssClasses - CSS classes of component
   * @source
   */
  constructor(
    screenNames: string[],
    isFlag: boolean,
    ecoMode?: string,
    cssClasses?: string[],
  ) {
    super(CustomNav.name, undefined, cssClasses);
    const transNavRenderObject = this._subRenderObjects.get(
      ScreenNames[TranslationScreen.name],
    );
    if (transNavRenderObject != undefined) {
      transNavRenderObject.icon.iconName = isFlag ? "fr" : "trad";
      transNavRenderObject.icon.fileName = isFlag
        ? "trans-icons"
        : "misc-icons";
      if (isFlag) transNavRenderObject.icon.cssClasses?.push("border-flags");
    }
    const navSubRenderObjects: NavButtonSubRenderObject[] = screenNames
      .map((screenName) => {
        const subRenderObject: NavButtonSubRenderObject =
          this._subRenderObjects.get(screenName)!;
        return subRenderObject;
      })
      .filter((subRenderObject) => subRenderObject != undefined)
      .sort((a, b) => a.position - b.position);

    const ulContainer = new ListContainer(
      navSubRenderObjects.map((navSubRenderObject) => {
        let navIconName = undefined;
        if (navSubRenderObject.buttonId === "eco-screen-nav") {
          navIconName =
            ecoMode === LisioCategoryNames.LIGHT_ECOLOGICAL
              ? "Dark"
              : navSubRenderObject.icon.iconName;
        } else {
          navIconName = navSubRenderObject.icon.iconName;
        }

        const navButton: CustomButton = new CustomButton(
          navSubRenderObject.buttonId,
          [
            new CustomIcon(
              navSubRenderObject.icon.fileName,
              navIconName,
              navSubRenderObject.icon.cssClasses,
            ),
          ],
          (e) => {
            ScreenManager.current.changeScreen(
              navSubRenderObject.nextScreenName,
              e.detail,
            );
          },
          navSubRenderObject.cssClasses,
          navSubRenderObject.aria,
          navSubRenderObject.role,
          navSubRenderObject.tabindex,
          [
            {
              triggerer: "keydown",
              handler: (e: KeyboardEvent) => {
                const navString = "-nav";
                const screenName =
                  navSubRenderObject.buttonId.split(navString)[0];
                const screenNavButtonIndex =
                  this._navButtons.indexOf(screenName);
                switch (e.code) {
                  case "ArrowRight":
                    if (screenNavButtonIndex < this._navButtons.length - 1) {
                      (
                        this.findChildById(
                          this._navButtons[screenNavButtonIndex + 1] +
                            navString,
                        )!.htmlElement as HTMLElement
                      ).focus();
                    }
                    break;
                  case "ArrowLeft":
                    if (screenNavButtonIndex > 0) {
                      (
                        this.findChildById(
                          this._navButtons[screenNavButtonIndex - 1] +
                            navString,
                        )!.htmlElement as HTMLElement
                      ).focus();
                    }
                    break;
                  case "Home":
                    (
                      this.findChildById(this._navButtons[0] + navString)!
                        .htmlElement as HTMLElement
                    ).focus();
                    break;
                  case "End":
                    (
                      this.findChildById(
                        this._navButtons[this._navButtons.length - 1] +
                          navString,
                      )!.htmlElement as HTMLElement
                    ).focus();
                    break;
                  case "Enter":
                    e.preventDefault();
                    e.target?.dispatchEvent(new Event("click"));
                    break;
                  case "Space":
                    e.preventDefault();
                    e.target?.dispatchEvent(new Event("click"));
                    break;
                }
              },
            },
          ],
        );
        this._navButtons.push(ScreenNames[navSubRenderObject.nextScreenName]);
        return navButton;
      }),
    );
    this._children.push(ulContainer);
  }
}

new LisioComponentStyles(CustomNav.name, customNavStyles);

export { CustomNav };
