import { _IdGenerator } from '@angular/cdk/a11y';
import { ChangeDetectionStrategy, Component, computed, effect, inject, input, model, signal, } from '@angular/core';
import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, } from '@angular/forms';
import { FastSVGComponent } from "./fast-svg.component";

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

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

type VALUE_TYPE = boolean | undefined | null;

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

type LABEL_ALIGN = 'start' | 'end';

type ToggleType = 'basic' | 'allow-deny' | 'show-hide' | 'combine-separate' | 'deny-allow';

@Component({
  selector: 'fast-toggle',
  imports: [FastSVGComponent],
  template: `
  <div class="flex items-center justify-center gap-2 cursor-pointer [&.start]:flex-row-reverse [&.disabled_.toggle]:text-gray-400 [&.disabled_.toggle]:bg-gray-300 [&.disabled_label]:text-gray-400">
    <button
      [disabled]="disabled()"
      [id]="buttonId"
      role="switch"
      [class]="conditionalClasses()"
      [class.checked]="value()"
      [attr.aria-checked]="value()"
      (click)="toggle()"
    >
      <div [class]="this.thumbClasses()"></div>
        <div class="flex h-6 items-center text-xs absolute invisible top-px left-1 in-[.checked_&]:visible">
          @if (onIcon()) {
            <fast-svg [icon]="onIcon()" [size]="20" [strokeColor]="'none'" [color]="onIcon() === 'pdfFile' ? 'black' : 'currentColor'" />
          } @else {
            {{ onLabel() }}
          }
        </div>
        <div class="flex h-6 items-center text-xs absolute top-px right-2 in-[.checked_&]:invisible">
          @if (offIcon()) {
            <fast-svg [icon]="offIcon()" [size]="20" [strokeColor]="'none'" [color]="offIcon() === 'excelFile' ? 'black' : 'currentColor'"/>
          } @else {
            {{ offLabel() }}
          }
        </div>
    </button>
    <label
      [htmlFor]="buttonId"
      [id]="labelId"
      class="cursor-pointer"
    >
      <ng-content></ng-content>
    </label>
  </div>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: FastToggleComponent,
    },
    {
      provide: NG_VALIDATORS,
      useExisting: FastToggleComponent,
      multi: true,
    },
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    role: 'group',
    '[class.disabled]': 'disabled()',
    '[class.checked]': 'value()',
    '[class.start]': 'labelAlign() === "start"',
    '[attr.aria-labelledby]': 'labelId',
  },
})
export class FastToggleComponent implements ControlValueAccessor, Validator {
  labelAlign = input<LABEL_ALIGN>('end');
  required = input<boolean>(false);
  type = input<ToggleType>('basic');
  onLabel = input<string>();
  offLabel = input<string>();
  onIcon = input<string>(null);
  offIcon = input<string>(null);
  isTrue: boolean = false;
  checked = input<boolean>(false);

  value = model<VALUE_TYPE>(undefined);
  private _idGenerator = inject(_IdGenerator);

  buttonId = this._idGenerator.getId('switch-button-');
  labelId = this._idGenerator.getId('switch-label-');
  disabled = signal<boolean>(false);

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

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

  toggle() {
    if (this.disabled()) {
      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): void {
    this.disabled.set(isDisabled);
  }

  validate(control: AbstractControl<boolean>): ValidationErrors | null {
    const hasRequiredValidator = this.required();
    return hasRequiredValidator && control.value !== true
      ? { required: true }
      : null;
  }

  thumbClasses = computed(() => {
    const classes = [] as string[];
    classes.push(
      "w-6",
      "h-6",
      "rounded-full",
      "absolute",
      "[.checked_&]:bg-white",
      "absolute",
      "top-px",
      "left-px",
      "transition-all",
      "duration-300",
      "[.checked_&]:left-[calc(100%-24px)]",
    );

    if (this.type() == 'basic') {
      classes.push('bg-base-gray-1000')
    }
    else {
      classes.push('bg-white');
    }

    return classes;
  })

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

    classes.push(...this.getCommonClasses());
    if (!this.disabled()) {
      classes.push(...this.getLightBaseClasses(this.type()));
      classes.push(...this.getDarkBaseClasses(this.type()));
    }

    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() {
    const baseClasses = [
      "flex",
      "grow",
      "w-14",
      "h-7.5",
      "rounded-2xl",
      "relative",
      "transition-colors",
      "duration-300",
      "border-2",
      "cursor-pointer",
      "focus:ring-4",
      "focus:ring-offset-2",
      "focus:outline-0",
      "[&:invalid]:outline-1",
      "[&:invalid]:outline-red-500",
    ];

    return baseClasses;
  }

  getLightBaseClasses(type: ToggleType) {
    const lightBase = [
      "text-base-black-1000",
      "border-base-gray-500",
      "ring-base-gray-750/50"];
    if (type === 'allow-deny') {
      lightBase.push(
        "bg-gradient-to-b",
        "from-base-white-250",
        "via-red-400",
        "to-red-300",
        "[&.checked]:bg-gradient-to-b",
        "[&.checked]:from-base-white-250",
        "[&.checked]:via-green-400",
        "[&.checked]:to-green-300",
      );
    }
    else if (type === 'deny-allow') {
      lightBase.push(
        "bg-gradient-to-b",
        "from-base-white-250",
        "via-green-400",
        "to-green-300",
        "[&.checked]:bg-gradient-to-b",
        "[&.checked]:from-base-white-250",
        "[&.checked]:via-red-400",
        "[&.checked]:to-red-300",
      );
    }
    else {
      lightBase.push(
        "bg-gradient-to-b",
        "from-base-white-1000",
        "via-base-white-1000",
        "to-to-base-white-250",
        "[&.checked]:bg-gradient-to-b",
        "[&.checked]:from-base-blue-1000",
        "[&.checked]:via-base-blue-1000",
        "[&.checked]:to-base-blue-250",
        "ring-base-gray-750/50"
      );
    }
    return lightBase;
  }

  getDarkBaseClasses(type: ToggleType) {
    const darkBase = [
      "dark:text-base-white-500",
      "dark:border-alt-blue-250",];
    if (type === 'allow-deny') {
      darkBase.push(
        "dark:bg-gradient-to-b",
        "dark:from-alt-gray-250",
        "dark:via-red-700",
        "dark:to-red-600",
        "dark:[&.checked]:bg-gradient-to-b",
        "dark:[&.checked]:from-alt-gray-250",
        "dark:[&.checked]:via-green-700",
        "dark:[&.checked]:to-green-600",
        "dark:ring-base-gray-750/50");
    }
    else if (type === 'deny-allow') {
      darkBase.push(
        "dark:bg-gradient-to-b",
        "dark:from-alt-gray-250",
        "dark:via-green-700",
        "dark:to-green-600",
        "dark:[&.checked]:bg-gradient-to-b",
        "dark:[&.checked]:from-alt-gray-250",
        "dark:[&.checked]:via-red-700",
        "dark:[&.checked]:to-red-600",
        "dark:ring-base-gray-750/50");
    }
    else {
      darkBase.push(
        "dark:bg-gradient-to-b",
        "dark:from-alt-gray-1000",
        "dark:via-alt-gray-1000",
        "dark:to-alt-gray-500",
        "dark:[&.checked]:bg-gradient-to-b",
        "dark:[&.checked]:from-base-blurple-500",
        "dark:[&.checked]:via-base-blurple-500",
        "dark:[&.checked]:to-alt-blue-250",
        "dark:ring-base-gray-750/50");
    }
    return darkBase;
  }
}
