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

import { LisioNumericParameterNames } from "@lisio/lisio-profils";
import { TextsContainer } from "../../containers/texts-container/texts-container";
import { CustomButton } from "../../custom-button/custom-button";
import { CustomIcon } from "../../custom-icon/custom-icon";
import { CustomText } from "../../custom-text/custom-text";
import { ElementsContainer } from "../../containers/elements-container/elements-container";
import { InputManager } from "../../../../managers/input-manager";
import { NumberInput } from "./input/number-input";
import { customSliderStyles } from "./custom-slider.css";
import { LisioComponent, LisioComponentStyles } from "@lisio/lisio-engine";
import { MergedCategoriesNames } from "../../../screens/screen-types";

/**
 * Class representing a slider component extending LisioComponent.\
 * It aims to represent a slider component.\
 * A slider component is basically a component to display a number input as a slider.\
 */
class CustomSlider extends LisioComponent {
  /**
   * Private attribute to store title id
   * @source
   */
  private _titleId: string;
  /**
   * Private attribute to store parameter name
   * @source
   */
  private _parameterName: LisioNumericParameterNames;
  /**
   * Private attribute to store default value
   * @source
   */
  private _defaultValue: number;
  /**
   * Private attribute to store current value
   * @source
   */
  private _currentValue: number;
  /**
   * Private attribute to store min value
   * @source
   */
  private _minValue: number;
  /**
   * Private attribute to store max value
   * @source
   */
  private _maxValue: number;
  /**
   * Private attribute to store step
   * @source
   */
  private _step: number;
  /**
   * Private attribute to store state of input
   * @source
   */
  private _isEnabled: boolean;
  /**
   * Private attribute to store input
   * @source
   */
  private _input: NumberInput;

  /**
   * Private method to increment slider value
   * @returns Returns nothing
   * @source
   */
  private incrementSlider(): void {
    const newValue: number = this._input.value + this._step;
    if (newValue <= this._maxValue) {
      (this._input.htmlElement as HTMLInputElement).value = newValue.toFixed(2);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("change"),
      );
    } else {
      (this._input.htmlElement as HTMLInputElement).value =
        this._maxValue.toFixed(0);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("change"),
      );
    }
  }

  /**
   * Public method to be called after render
   * @returns Returns nothing
   * @source
   */
  public postRender(): void {
    if (this._currentValue !== this._defaultValue) {
      (
        this._htmlElement?.querySelector(
          ".lisio-elements-container",
        ) as HTMLElement
      ).classList.add("active");
    }
  }

  /**
   * Private method to decrement slider value
   * @returns Returns nothing
   * @source
   */
  private decrementSlider(): void {
    const newValue: number = this._input.value - this._step;
    if (newValue >= this._minValue) {
      (this._input.htmlElement as HTMLInputElement).value = newValue.toFixed(2);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("change"),
      );
    } else {
      (this._input.htmlElement as HTMLInputElement).value =
        this._minValue.toFixed(0);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("change"),
      );
    }
  }

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a slider in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
      <div id="${this._id}" class="lisio-custom-slider ${
        this._isEnabled ? "active" : ""
      } ${this.renderClasses()}" style="background-position-x: ${
        100 - (this._currentValue / this._maxValue) * 100
      }%">
        <children></children>
      </div>
    `;
  }

  /**
   * Constructor of class {@link CustomSlider | CustomSlider}
   * @param {string} id - Id of component
   * @param {string} titleId - Title id of text
   * @param {LisioStringParameterNames | "users-choice"} parameterName - Name of functionality
   * @param {number} defaultValue - Default value of input
   * @param {number} currentValue - Value of input
   * @param {number} minValue - Min value of input
   * @param {number} maxValue - Max value of input
   * @param {number} step - Step of input
   * @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} cssClasses - CSS classes of component
   * @param {object | undefined} icon - Icon object
   * @property {string} icon.fileName - Icon file name
   * @property {string} icon.iconName - Icon name
   * @param {string[] | undefined} iconClasses - CSS classes of icon
   * @source
   */
  constructor(
    id: string,
    titleId: string,
    parameterName: LisioNumericParameterNames,
    defaultValue: number,
    currentValue: number,
    minValue: number,
    maxValue: number,
    step: number,
    screenName: string,
    categoryName?: MergedCategoriesNames,
    parentScreenName?: string,
    infobox?: ElementsContainer,
    cssClasses?: string[],
    icon?: {
      fileName: string;
      iconName: string;
    },
    iconClasses?: string[],
  ) {
    super(CustomSlider.name, id, cssClasses);
    this._titleId = titleId;
    this._parameterName = parameterName;
    this._defaultValue = defaultValue;
    this._currentValue = currentValue;
    this._isEnabled = defaultValue != currentValue;
    this._minValue = minValue;
    this._maxValue = maxValue;
    this._step = step;
    infobox?.showHideElement(currentValue != defaultValue);

    const handler = async (event: any) => {
      await InputManager.current.disableProfileLinkToParameter(parameterName);
      if (Number.parseFloat(event.target.value) > maxValue) {
        event.target.value = maxValue;
        this._currentValue = maxValue;
      }
      if (Number.parseFloat(event.target.value) < minValue) {
        event.target.value = minValue;
        this._currentValue = minValue;
      }
    };
    this._input = new NumberInput(
      this._currentValue,
      this._defaultValue,
      this._minValue,
      this._maxValue,
      this._step,
      this._parameterName,
      handler,
      `${parameterName}-input`,
      screenName,
      this,
      categoryName,
      parentScreenName,
      infobox,
      currentValue === defaultValue ? undefined : ["active"],
    );
    if (icon) {
      this._children.push(
        new CustomIcon(icon.fileName, icon.iconName, iconClasses),
      );
    }
    this._children.push(
      new TextsContainer([
        new CustomText(
          "label",
          this._titleId,
          false,
          ["text-center", "medium"],
          `${parameterName}-input`.replace(/_/g, "-"),
        ),
      ]),
      new ElementsContainer(
        [
          new CustomButton(
            `${this._titleId}-slider-minus`,
            [new CustomIcon("misc-icons", "Minus")],
            this.decrementSlider.bind(this),
            ["small", "secondary"],
            {
              label: {
                id: "decrease",
              },
              controls: id.replace(/button/g, "input"),
            },
            undefined,
            "-1",
          ),
          new TextsContainer([this._input], ["slider-text-value"]),
          new CustomButton(
            `${this._titleId}-slider-plus`,
            [new CustomIcon("misc-icons", "Plus")],
            this.incrementSlider.bind(this),
            ["small", "secondary"],
            {
              label: {
                id: "increase",
              },
              controls: id.replace(/button/g, "input"),
            },
            undefined,
            "-1",
          ),
        ],
        undefined,
        undefined,
      ),
    );
    if (currentValue != defaultValue) {
      InputManager.current.addOrRemoveEnabledInput(parameterName, true);
    }
  }
}

new LisioComponentStyles(CustomSlider.name, customSliderStyles);

export { CustomSlider };
