// IMPORTANT: This file is credited to NGVerse's open source CheckboxComponent, modified and stylized with our design system.
// The original source can be found at: https://www.ngverse.dev/doc/ui/checkbox

import { _IdGenerator } from '@angular/cdk/a11y';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, effect, inject, input, signal } from '@angular/core';
import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../../app.config';
import * as util from '../utils/util';

type VALUE_TYPE = boolean | undefined | null;

type OnChangeFunction = ((_: unknown) => void) | undefined;

type OnTouchedFunction = (() => void) | undefined;

type ValidatorChangeFunction = (() => void) | undefined;

type LABEL_ALIGN = 'start' | 'end';

@Component({
  selector: 'fast-checkbox',
  imports: [FAST_KENDO_COMMON, FAST_PAGE_COMMON],
  template: `
  <div [class]="conditionalClasses()">
    <input
      [disabled]="isDisabled()"
      (change)="toggle()"
      class="peer absolute top-0 right-0 bottom-0 left-0 z-auto h-full w-full opacity-0 cursor-pointer"
      [id]="inputId()"
      [checked]="value()"
      type="checkbox"
    />
    @if(value()){
    <div class="relative h-3 w-3 cursor-pointer
    bg-base-blue-500 text-base-black-1000
    dark:bg-global-bar-light dark:text-base-white-250
    "
    (click)="toggle()">
      <kendo-svg-icon
      [icon]="icons.checkIcon" />
    </div>
    }
  </div>
  <div class="select-none">
    <fast-label [htmlFor]="inputId()">
      {{labelText()}}
    </fast-label>
    <ng-content></ng-content>
  </div>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    multi: true,
    useExisting: FastCheckboxComponent,
  },
  {
    provide: NG_VALIDATORS,
    useExisting: FastCheckboxComponent,
    multi: true,
  }],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    class:
      'inline-flex items-center gap-1 [.ng-invalid.ng-touched]:text-danger [.disabled]:text-disabled-foreground [.start]:flex-row-reverse',
    '[class.checked]': 'value()',
    '[class.start]': 'labelAlign() === "start"',
  },
})
export class FastCheckboxComponent implements ControlValueAccessor, Validator {
  cdr = inject(ChangeDetectorRef);
  util = util;
  icons = util.icons;

  labelText = input<string>('');
  labelAlign = input<LABEL_ALIGN>('end');
  required = input<boolean>(false);
  inputId = input(inject(_IdGenerator).getId('checkbox-'));
  disabled = input<boolean>(false);

  value = signal<VALUE_TYPE>(undefined);

  // Internal signal to track form control disabled state
  private formControlDisabled = signal<boolean>(false);

  // Computed signal that combines input disabled state and form control disabled state
  isDisabled = computed(() => this.disabled() || this.formControlDisabled());


  private _registerOnChangefn: OnChangeFunction;
  private _onTouchedfn: OnTouchedFunction;
  private _validatorChangefn: ValidatorChangeFunction;

  constructor() {
    effect(() => {
      this.required();
      this._validatorChangefn?.();
    });
  }

  toggle() {
    if (this.isDisabled()) {
      return;
    }
    this._onTouchedfn?.();
    const newValue = !this.value();
    this.value.set(newValue);
    this._registerOnChangefn?.(newValue);
  }

  writeValue(obj: unknown): void {
    this.value.set(!!obj);
  }
  registerOnChange(fn: OnChangeFunction): void {
    this._registerOnChangefn = fn;
  }

  registerOnValidatorChange(fn: () => void): void {
    this._validatorChangefn = fn;
  }

  registerOnTouched(fn: OnTouchedFunction): void {
    this._onTouchedfn = fn;
  }

  setDisabledState(isDisabled: boolean) {
    this.formControlDisabled.set(isDisabled);
    this.cdr.markForCheck();
  }

  validate(control: AbstractControl<boolean>): ValidationErrors | null {
    const hasRequiredValidator = this.required();

    return hasRequiredValidator && control.value !== true
      ? { required: true }
      : null;
  }

  conditionalClasses = computed(() => {
    const classes = [] as string[];

    classes.push(...this.getCommonClasses());
    classes.push(...this.getLightClasses());
    classes.push(...this.getDarkClasses());
    if (!this.isDisabled()) {
      classes.push(...this.getActiveClasses());
    }

    const conditionalClasses = this.getConditionalClassesFromArrays(classes);
    return conditionalClasses;
  });


  getConditionalClassesFromArrays(classArray: string[]): { [key: string]: boolean } {
    const classes: { [key: string]: boolean } = {};
    classArray.forEach(className => {
      classes[className] = true;
    });
    return classes;
  }

  getCommonClasses() {
    return [
      "relative",
      "h-4",
      "w-4",
      "border-2",
      "rounded-xs",
      "cursor-pointer",
      "[&_.k-svg-icon]:flex",
      "[&_.k-svg-icon]:w-3",
      "[&_.k-svg-icon]:h-3",
    ]
  }

  getActiveClasses() {
    return [
      "active:ring-2",
      "active:round",
    ];
  }

  getLightClasses() {
    return [
      "border-base-gray-750",
      "ring-base-blue-500",
    ]
  }

  getDarkClasses() {
    return [
      "dark:border-alt-blue-250",
      "dark:ring-alt-white-250",
    ];
  }
}
