export interface InputStepperConfig {
  nonEditableInput?: boolean;
  min?: number;
  max?: number;
  defaultValue?: number;
}

export const defaultConfig: Required<InputStepperConfig> = {
  nonEditableInput: false,
  min: 1,
  max: 999,
  defaultValue: 1,
};

export default class InputStepperStatic {
  element: HTMLElement;
  config: Required<InputStepperConfig>;
  input: HTMLInputElement | null;
  numberDisplay: HTMLElement | null;
  add: HTMLButtonElement | null;
  remove: HTMLButtonElement | null;

  constructor(element: HTMLElement, config?: InputStepperConfig) {
    this.element = element;
    this.config = { ...defaultConfig, ...config };
    this.input = null;
    this.numberDisplay = null;
    this.add = null;
    this.remove = null;
    (this.element as any).ODS_InputStepper = this;
    this.handleButtonClick = this.handleButtonClick.bind(this);
    this.handleInputClick = this.handleInputClick.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleButtonState = this.handleButtonState.bind(this);

    this.init();
  }

  handleInputClick(): void {
    if (this.input) {
      this.input.select();
    }
  }

  handleButtonState(): void {
    const { min, max } = this.config;
    const value = !this.config.nonEditableInput
      ? parseInt(this.input?.value ?? "0", 10)
      : parseInt(this.numberDisplay?.textContent ?? "0", 10);

    if (this.add && typeof max === "number" && value >= max) {
      this.add.setAttribute("aria-disabled", "true");
      this.add.setAttribute("disabled", "true");
    } else if (this.add) {
      this.add.removeAttribute("aria-disabled");
      this.add.removeAttribute("disabled");
    }
    if (this.remove && typeof min === "number" && value <= min) {
      this.remove.setAttribute("aria-disabled", "true");
      this.remove.setAttribute("disabled", "true");
    } else if (this.remove) {
      this.remove.removeAttribute("aria-disabled");
      this.remove.removeAttribute("disabled");
    }
  }

  handleInputChange(e: Event): void {
    const { min, max } = this.config;
    const target = e.target as HTMLInputElement;
    if (target) {
      if (min && Number(target.value) <= min) {
        target.value = String(min);
      }
      if (max && Number(target.value) >= max) {
        target.value = String(max);
      }
    }
  }

  handleButtonClick(e: MouseEvent): void {
    const { min, max } = this.config;
    const target = e.target as HTMLElement;
    let value = !this.config.nonEditableInput
      ? parseInt(this.input?.value ?? "0", 10)
      : parseInt(this.numberDisplay?.textContent ?? "0", 10);

    if (target.classList.contains("input-stepper__button--minus")) {
      if (min && value <= min) {
        if (target.getAttribute("disabled") === "true") return;
        target.setAttribute("aria-disabled", "true");
        target.setAttribute("disabled", "true");
        return;
      }
      value -= 1;
    }
    if (target.classList.contains("input-stepper__button--plus")) {
      if (max && value >= max) {
        if (target.getAttribute("disabled") === "true") return;
        target.setAttribute("aria-disabled", "true");
        target.setAttribute("disabled", "true");
        return;
      }
      value += 1;
    }
    if (!this.config.nonEditableInput && this.input) {
      this.input.value = String(value);
    } else if (this.numberDisplay) {
      this.numberDisplay.textContent = String(value);
    }
    this.handleButtonState();
  }

  init(): void {
    const configAttr = this.element.getAttribute("data-inputstepper");
    let parsedConfig: Partial<InputStepperConfig> = {};
    try {
      if (configAttr && configAttr.trim().startsWith("{")) {
        parsedConfig = JSON.parse(configAttr);
      }
    } catch {
      parsedConfig = {};
    }
    this.config = {
      ...this.config,
      ...parsedConfig,
    };

    if (!this.config.nonEditableInput) {
      this.input = this.element.querySelector(
        ".input-stepper__field",
      ) as HTMLInputElement;
      if (this.input) this.input.value = String(this.config.defaultValue);
    } else {
      this.numberDisplay = this.element.querySelector(".input-stepper__number");
      if (this.numberDisplay)
        this.numberDisplay.textContent = String(this.config.defaultValue);
    }
    this.add = this.element.querySelector(
      ".input-stepper__button--plus",
    ) as HTMLButtonElement;
    this.remove = this.element.querySelector(
      ".input-stepper__button--minus",
    ) as HTMLButtonElement;

    const { defaultValue } = this.config;

    if (defaultValue !== undefined) {
      if (!this.config.nonEditableInput && this.input) {
        this.input.value = String(defaultValue);
      } else if (this.numberDisplay) {
        this.numberDisplay.textContent = String(defaultValue);
      }
    }

    if (!this.element.classList.contains("input-stepper--disabled")) {
      if (this.add) this.add.addEventListener("click", this.handleButtonClick);
      if (this.remove)
        this.remove.addEventListener("click", this.handleButtonClick);
      if (!this.config.nonEditableInput && this.input) {
        this.input.addEventListener("click", this.handleInputClick);
        this.input.addEventListener("change", this.handleInputChange);
      }
      this.handleButtonState();
    }
  }

  update(): void {
    this.destroy();
    this.init();
  }

  destroy(): void {
    if (!this.config.nonEditableInput && this.input) {
      this.input.removeEventListener("click", this.handleInputClick);
      this.input.removeEventListener("change", this.handleInputChange);
    }
    if (this.add) this.add.removeEventListener("click", this.handleButtonClick);
    if (this.remove)
      this.remove.removeEventListener("click", this.handleButtonClick);
  }

  static getInstance(el: HTMLElement): InputStepperStatic | null {
    return el && (el as any).ODS_InputStepper
      ? (el as any).ODS_InputStepper
      : null;
  }
}
