import { Component, Input, forwardRef, OnInit } from "@angular/core";
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
  NG_VALIDATORS,
  Validator,
  AbstractControl,
  ValidationErrors,
  FormControl,
  ReactiveFormsModule,
} from "@angular/forms";
import {
  Observable,
  of,
  map,
  startWith,
  debounceTime,
  distinctUntilChanged,
  filter,
  switchMap,
  tap,
} from "rxjs";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import {
  MatAutocompleteModule,
  MatAutocompleteSelectedEvent,
  MatAutocompleteTrigger,
} from "@angular/material/autocomplete";
import { MatOptionModule } from "@angular/material/core";

@Component({
  selector: "app-custom-autocomplete",
  standalone: true,
  templateUrl: "./custom-autocomplete.component.html",
  styleUrls: ["./custom-autocomplete.component.scss"],
  imports: [
    CommonModule,
    FormsModule,
    MatFormFieldModule,
    MatInputModule,
    ReactiveFormsModule,
    MatAutocompleteModule,
    MatOptionModule,
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomAutocompleteComponent),
      multi: true,
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CustomAutocompleteComponent),
      multi: true,
    },
  ],
})
export class CustomAutocompleteComponent
  implements ControlValueAccessor, Validator, OnInit
{
  @Input() label!: string;
  @Input() options: { value: any; label: string }[] = [];
  @Input() fieldKey!: string;
  @Input() initialValue: any;
  @Input() fetchOptions!: (
    search: string
  ) => Observable<{ value: any; label: string }[]>;

  filteredOptions$: Observable<any[]> = of([]);
  searchControl = new FormControl("");

  innerValue: any;
  onChange = (_: any) => {};
  onTouched = () => {};

  private _setInitialValue(value: any) {
    if (
      typeof value === "object" &&
      value.value !== undefined &&
      value.label !== undefined
    ) {
      this.innerValue = value;
      this.searchControl.setValue(value.label, { emitEvent: false });
    } else if (this.options.length > 0) {
      const match = this.options.find((opt) => opt.value === value);
      if (match) {
        this.innerValue = match;
        this.searchControl.setValue(match.label, { emitEvent: false });
      }
    }
  }

  ngOnInit() {
    if (this.fetchOptions) {
      this.filteredOptions$ = this.searchControl.valueChanges.pipe(
        startWith(""),
        debounceTime(300),
        distinctUntilChanged(),
        tap((val) => {
          if (typeof val !== "object") {
            this.onChange(val);
          }
        }),
        switchMap((search) => {
          if (typeof search === "object" && search !== null) {
            return of([search]);
          }
          return this.fetchOptions(search as string);
        })
      );
    } else {
      this.filteredOptions$ = this.searchControl.valueChanges.pipe(
        startWith(""),
        map((val: any) => {
          if (typeof val === "object" && val !== null) {
            return [val];
          }
          return this.filterOptions(val, this.options);
        })
      );
    }
  }

  writeValue(value: any): void {
    if (value !== undefined && value !== null) {
      this._setInitialValue(value);
    } else {
      this.innerValue = null;
      this.searchControl.setValue("", { emitEvent: false });
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {}

  onInput(event: Event) {
    const input = event.target as HTMLInputElement;
    const value = input.value;

    this.filteredOptions$ = of(this.options).pipe(
      map((options) => this.filterOptions(value, options))
    );

    this.onChange(value);
  }

  onOptionSelected(event: MatAutocompleteSelectedEvent) {
    const selected = event.option.value;
    this.innerValue = selected;
    this.searchControl.setValue(selected.label, { emitEvent: false });
    this.onChange(selected.value);
  }

  onFocus(trigger: MatAutocompleteTrigger) {
    if (this.fetchOptions) {
      this.fetchOptions("").subscribe((options) => {
        this.filteredOptions$ = of(options);
        trigger.openPanel();
      });
    } else {
      this.filteredOptions$ = of(this.filterOptions("", this.options));
      trigger.openPanel();
    }
  }
  filterOptions(search: string, options: any[]): any[] {
    if (!search) return options.slice(0, 10);
    const lower = search.toLowerCase();
    return options
      .filter(
        (opt) =>
          opt.label.toLowerCase().includes(lower) ||
          opt.value.toString().includes(lower)
      )
      .slice(0, 10);
  }

  displayFn(option: any): string {
    if (!option) return "";
    if (typeof option === "object" && option.label) {
      return option.label;
    }
    if (this.options) {
      const found = this.options.find((o) => o.value === option);
      return found ? found.label : option.toString();
    }
    return option.toString();
  }

  validate(control: AbstractControl): ValidationErrors | null {
    return null;
  }
}
