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

import {
  LisioBooleanParameterNames,
  LisioParameterNames,
  LisioProfileNames,
} from "@lisio/lisio-profils";
import { InputManager } from "../../../managers/input-manager";
import { LisioComponent } from "@lisio/lisio-engine";
import { MergedCategoriesNames } from "../../screens/screen-types";
import { ScreenManager } from "../../../managers/screen-manager";
import { TranslationController } from "../../../controllers/translation/translation-controller";
import { ElementsContainer } from "../containers/elements-container/elements-container";

/**
 * Abstract class representing an input component extending LisioComponent.\
 * It aims to represent an input component.\
 * An input component is basically a component to defines the concept of input.\
 */
abstract class CustomInput extends LisioComponent {
  /**
   * Protected attribute to store aria hidden attribute value
   * @source
   */
  protected _ariaHidden?: boolean;

  /**
   * Protected attribute to store category name containing parameter or profile related to this input
   * @source
   */
  protected _categoryName?: MergedCategoriesNames;

  /**
   * Protected attribute to store screen name containing this input
   * @source
   */
  protected _screenName: string;

  /**
   * Protected attribute to store parent screen name in screen tree
   * @source
   */
  protected _parentScreenName?: string;

  /**
   * Protected attribute to store infobox linked to this input
   * @source
   */
  protected _infobox?: ElementsContainer;

  /**
   * @async
   * Public abstract method to toggle input
   * @typeParam T - Indicates type value expected :
   *  * Boolean : {@link Src/Components/CustomInput.CheckboxInput | CheckboxInput} and {@link Src/Components/CustomInput.RadioInput | RadioInput}
   *  * Number : {@link Src/Components/CustomInput.NumberInput | NumberInput}
   *  * String : {@link Src/Components/CustomInput.TextInput | TextInput}
   * @param {T} value - Value of input
   * @param {boolean} hasToHandleOnChange - Indicates if input has to handle on change
   * @param {boolean} hasToUpdateUser - Indicates if input has to update user
   * @param {boolean} hasToEnableActiveInput - Indicates if has to enable active input
   * @returns Returns a promise containing nothing
   * @source
   */
  public abstract toggle<T extends boolean | number | string>(
    value: T,
    hasToHandleOnChange: boolean,
    hasToUpdateUser: boolean,
    hasToEnableActiveInput: boolean,
  ): Promise<void>;

  /**
   * Protected method to centrelize some toggle behavior of an input. It would be better to move this in toggle but it's need some changes.
   * @param {LisioParameterNames | LisioProfileNames | "users-choice" | "rename-user"} name - Name of parameter, profile or other functionality related to this input
   * @param {boolean} isEnabled - Indicates if input is enabled or not
   * @param {boolean} hasToEnableActiveInput - Indicates if has to enable active input
   * @returns Returns nothing
   * @source
   */
  protected generalToggleBehavior(
    name:
      | LisioParameterNames
      | LisioProfileNames
      | "users-choice"
      | "rename-user",
    isEnabled: boolean,
    hasToEnableActiveInput: boolean,
  ): void {
    if (this._infobox != undefined) {
      this._infobox.showHideElement(isEnabled);
    }
    if (name != "users-choice" && name != "rename-user") {
      InputManager.current.addOrRemoveEnabledInput(name, isEnabled);
      if (
        name !== LisioBooleanParameterNames.IS_ACTIVE &&
        hasToEnableActiveInput
      ) {
        InputManager.current.toggleIsActiveInput(true, false);
      }
      if (
        this._categoryName != undefined &&
        this._parentScreenName != undefined
      ) {
        ScreenManager.current.breadcrumb(
          name,
          this._screenName,
          this._categoryName,
          this._parentScreenName,
          isEnabled,
        );
      }
    }
  }

  /**
   * Protected method to handle on change event
   * @returns Returns nothing
   * @source
   */
  protected abstract handleOnChange(): void;

  /**
   * Protected method to handle initialization
   * @param {boolean} hasToEnableActiveInput - Indicates if has to enable active input
   * @returns Returns nothing
   * @source
   */
  protected abstract handleOnInit(hasToEnableActiveInput: boolean): void;

  /**
   * Constructor of class {@link CustomInput | CustomInput}
   * @param {string} id - Id of component
   * @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 {LisioComponent | undefined} infobox - Infobox linked to this component
   * @param {string[] | undefined} cssClasses - CSS classes of component
   * @param {object | undefined} aria - Aria attributes
   * @property {object | undefined} aria.label - Aria label datas
   * @property {boolean | undefined} aria.label.noTranslate - Indicates if label need to be translated
   * @property {string} aria.label.id - Id of the text for aria label
   * @property {boolean | undefined} aria.hidden - Aria hidden
   * @source
   */
  constructor(
    id: string,
    screenName: string,
    categoryName?: MergedCategoriesNames,
    parentScreenName?: string,
    infobox?: ElementsContainer,
    cssClasses?: string[],
    attributes?: Map<string, string>,
    aria?: {
      label?: {
        noTranslate?: boolean;
        id: string;
      };
      hidden?: boolean;
    },
  ) {
    if (attributes) {
      if (aria) {
        if (aria.label) {
          attributes.set(
            "aria-label",
            aria.label.noTranslate
              ? aria.label.id
              : TranslationController.current.getTranslation(aria.label.id),
          );
        }
        if (aria.hidden != undefined) {
          attributes.set("aria-hidden", String(aria.hidden));
        }
      }
    }
    super(CustomInput.name, id.replace(/_/g, "-"), cssClasses, attributes);
    this._screenName = screenName;
    this._categoryName = categoryName;
    this._parentScreenName = parentScreenName;
    this._infobox = infobox;
  }
}

export { CustomInput };
