/**
 * @mergeTarget
 * @module Src/Components/CustomInput/CustomChoice
 */

import { LisioStringParameterNames } from "@lisio/lisio-profils";
import { CustomText } from "../../custom-text/custom-text";
import { CustomChoiceItem } from "./item/custom-choice-item";
import { CustomChoiceItemObject } from "../../component-types";
import { customChoiceStyles } from "./custom-choice.css";
import { LisioComponent, LisioComponentStyles } from "@lisio/lisio-engine";
import { MergedCategoriesNames } from "../../../screens/screen-types";
import { RadioInput } from "./input/radio-input";
import { ElementsContainer } from "../../containers/elements-container/elements-container";
import { voiceEvent } from "../../../../../misc/events";

/**
 * Class representing a choice list component extending LisioComponent.\
 * It aims to represent a choice list component.\
 * A choice list component is basically a component to display a choice list.\
 */
class CustomChoice extends LisioComponent {
  /**
   * Private attribute to store previous checked input
   */
  private _previousChecked?: RadioInput;

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a choice list in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
      <ul class="lisio-custom-choice ${this.renderClasses()}">
        <children></children>
      </ul>
    `;
  }

  /**
   * Public method to clear all children in list
   * @returns Returns nothing
   * @source
   */
  public clearChildren(): void {
    this._children = [];
    if (this._htmlElement != undefined) {
      for (const htmlChild of [...this._htmlElement.children]) {
        htmlChild.remove();
      }
    }
  }

  /**
   * Public method to dynamically add item in list
   * @param {LisioStringParameterNames | "users-choice"} parameterName - Functionality name of input
   * @param {CustomChoiceItemObject} item - Item to add
   * @param {string} screenName - Screen name including this input
   * @param {MergedCategoriesNames | undefined} categoryName - Category name containing parameter or profile related to this input
   * @param {string | undefined} parentScreenName - Parent screen name in screen tree
   * @param {ElementsContainer | undefined} infobox - Infobox linked to this component
   * @param {string[] | undefined} cssClassesItems - CSS class of item
   * @param {string[] | undefined} cssClassesLabelItems - CSS class of label
   * @returns Returns nothing
   * @source
   */
  public addItemInList(
    parameterName: LisioStringParameterNames | "users-choice",
    item: CustomChoiceItemObject,
    screenName: string,
    categoryName?: MergedCategoriesNames,
    parentScreenName?: string,
    infobox?: ElementsContainer,
    cssClassesItems?: string[],
    cssClassesLabelItems?: string[],
  ): void {
    const customChoiceItem: CustomChoiceItem = new CustomChoiceItem(
      parameterName,
      item.textId,
      item.value,
      item.isChecked,
      screenName,
      this.handleLastChecked.bind(this),
      categoryName,
      parentScreenName,
      infobox,
      item.icon,
      item.iconClasses,
      item.noTranslate,
      cssClassesItems,
      cssClassesLabelItems,
      item.noText,
    );
    this.addChildren([customChoiceItem], "append", this);
  }

  /**
   * Public method to change username in list of currest user
   * @param {string} currentUsername - Current username
   * @param {string} newUsername - New username
   * @returns Returns nothing
   * @source
   */
  public changeUsernameInList(
    currentUsername: string,
    newUsername: string,
  ): void {
    const input = this._htmlElement?.querySelector(
      `input[value="${currentUsername}"]`,
    );

    const label = input?.closest("label");
    input!.setAttribute("value", newUsername);
    label!.querySelector("p")!.textContent = newUsername;
    input!.setAttribute("id", `users-choice-${newUsername}-input`);
    label!.setAttribute("for", `users-choice-${newUsername}-input`);
    (
      this.findChildById(
        `users-choice-${currentUsername.toLowerCase()}-input`,
      ) as RadioInput
    ).id = `users-choice-${newUsername.toLowerCase()}-input`;
    (
      this.findChildById(
        `users-choice-${newUsername.toLowerCase()}-input`,
      ) as RadioInput
    ).value = newUsername;
  }

  /**
   * Public method to remove user in list
   * @param {string} username - Username of user to delete
   * @returns Returns nothing
   * @source
   */
  public removeUserFromList(username: string): void {
    const childToRemove: LisioComponent | undefined = this._children.find(
      (child) =>
        child.children.some(
          (subChild) => (subChild as RadioInput).value === username,
        ),
    );
    if (childToRemove != undefined) {
      this.removeChildren([childToRemove]);
      this._children[0]?.children[0]?.htmlElement?.click();
    }
  }

  /**
   * Public method to handle last checked input
   * @param {RadioInput} input - New radio input checked
   * @returns Returns nothing
   * @source
   */
  public handleLastChecked(input: RadioInput): void {
    if (
      this._previousChecked != undefined &&
      this._previousChecked.value !== input.value
    ) {
      this._previousChecked.toggle<boolean>(false, false, false, false);
    }
    this._previousChecked = input;
  }

  /**
   * Constructor of class {@link CustomChoice | CustomChoice}
   * @param {CustomChoiceItemObject} items - Children items
   * @param {LisioStringParameterNames | "users-choice"} parameterName - Functionality name
   * @param {string} screenName - Screen name including this input
   * @param {MergedCategoriesNames | undefined} categoryName - Category name containing parameter or profile related to this input
   * @param {string | undefined} parentScreenName - Parent screen name in screen tree
   * @param {ElementsContainer | undefined} infobox - Infobox linked to this component
   * @param {string | undefined} titleId - Title id of text
   * @param {string} defaultText - Default text id of default input. Default value "default-text"
   * @param {string[] | undefined} cssClasses - CSS classes of component
   * @param {string[] | undefined} cssClassesItems - CSS classes of items
   * @param {string[] | undefined} cssClassesLabelItems - CSS classes of items labels
   * @source
   */
  constructor(
    items: CustomChoiceItemObject[],
    parameterName: LisioStringParameterNames | "users-choice",
    screenName: string,
    categoryName?: MergedCategoriesNames,
    parentScreenName?: string,
    infobox?: ElementsContainer,
    titleId?: string,
    defaultText: string = "default-text",
    cssClasses?: string[],
    cssClassesItems?: string[],
    cssClassesLabelItems?: string[],
    // noText?: boolean,
  ) {
    super(CustomChoice.name, undefined, cssClasses);
    if (titleId != undefined) {
      this._children.push(new CustomText("p", titleId, false, ["medium"]));
    }
    const activeChoiceItemObject: CustomChoiceItemObject | undefined =
      items.find((item) => item.isChecked);
    if (parameterName != "users-choice") {
      items.unshift({
        textId: defaultText,
        value: "default",
        isChecked: activeChoiceItemObject == undefined,
        cssClasses: ["default-choice-item"],
      });
    }

    this._children.push(
      ...items.map((item) => {
        return new CustomChoiceItem(
          parameterName,
          item.textId,
          item.value,
          item.isChecked,
          screenName,
          this.handleLastChecked.bind(this),
          categoryName,
          parentScreenName,
          infobox,
          item.icon,
          item.iconClasses,
          item.noTranslate,
          item?.cssClasses ? item.cssClasses : cssClassesItems,
          cssClassesLabelItems,
          item.noText,
          item.specificLabel,
        );
      }),
    );
    if (
      parameterName === LisioStringParameterNames.DEEPL_TRANSLATION ||
      parameterName === LisioStringParameterNames.GOOGLE_TRANSLATION
    ) {
      window.addEventListener(voiceEvent.type, (e: any) => {
        const voicesIso = e.detail.voices;
        this._children.forEach((child) => {
          const customChoice = child as CustomChoiceItem;
          const isAvailable = voicesIso.some((value: string) =>
            value.startsWith(customChoice.input.value.toUpperCase()),
          );
          customChoice.langVoiceAvailable = isAvailable;
        });
      });
    }
  }
}

new LisioComponentStyles(CustomChoice.name, customChoiceStyles);

export { CustomChoice };
