import { Component, ElementRef, HostListener, Input, forwardRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-select',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './custom-select.html',
  styleUrls: ['./custom-select.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomSelectComponent),
      multi: true,
    },
  ],
})
export class CustomSelectComponent implements ControlValueAccessor {
  @Input() options: any[] = [];
  @Input() placeholder = 'Selecione uma opção';
  @Input() labelKey = 'label';
  @Input() valueKey = 'value';
  @Input() size: 'sm' | 'md' = 'md';
  @Input() disabled = false;
  @Input() searchable = false;
  @Input() searchPlaceholder = 'Pesquisar...';

  isOpen = false;
  value: any = null;
  searchTerm = '';

  private onChange: (value: any) => void = () => {};
  private onTouched: () => void = () => {};

  constructor(private host: ElementRef<HTMLElement>) {}

  writeValue(value: any): void {
    this.value = value;
  }

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

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

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
    if (this.disabled) this.isOpen = false;
  }

  get displayLabel(): string {
    const selected = this.findOption(this.value);
    if (selected !== undefined) return this.getOptionLabel(selected);
    if (this.value === null || this.value === undefined || this.value === '') return this.placeholder;
    return String(this.value);
  }

  get hasValue(): boolean {
    return !(this.value === null || this.value === undefined || this.value === '');
  }

  toggle(): void {
    if (this.disabled) return;
    this.isOpen = !this.isOpen;
    if (!this.isOpen) this.searchTerm = '';
  }

  close(): void {
    this.isOpen = false;
    this.searchTerm = '';
  }

  selectOption(option: any): void {
    if (this.disabled) return;
    const value = this.getOptionValue(option);
    this.value = value;
    this.onChange(value);
    this.onTouched();
    this.close();
  }

  isSelected(option: any): boolean {
    return this.getOptionValue(option) === this.value;
  }

  trackByValue = (_: number, option: any) => this.getOptionValue(option);

  get filteredOptions(): any[] {
    const opts = this.options || [];
    const term = this.normalizeText(this.searchTerm);
    if (!this.searchable || !term) return opts;
    return opts.filter((opt) => this.normalizeText(this.getOptionLabel(opt)).includes(term));
  }

  onSearchInput(value: string): void {
    this.searchTerm = value ?? '';
  }

  clearSearch(event?: Event): void {
    event?.stopPropagation();
    this.searchTerm = '';
  }

  onSearchKeydown(event: KeyboardEvent): void {
    event.stopPropagation();
  }

  private getOptionValue(option: any): any {
    if (option && typeof option === 'object') {
      return option[this.valueKey];
    }
    return option;
  }

  getOptionLabel(option: any): string {
    if (option && typeof option === 'object') {
      const v = option[this.labelKey];
      return v === undefined || v === null ? '' : String(v);
    }
    return option === undefined || option === null ? '' : String(option);
  }

  private findOption(value: any): any {
    return (this.options || []).find((o) => this.getOptionValue(o) === value);
  }

  private normalizeText(value: string): string {
    return (value ?? '')
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .toLowerCase()
      .trim();
  }

  @HostListener('document:click', ['$event'])
  onDocumentClick(event: MouseEvent): void {
    if (!this.isOpen) return;
    const target = event.target as Node | null;
    if (target && this.host.nativeElement.contains(target)) return;
    this.close();
  }

  @HostListener('document:keydown.escape')
  onEsc(): void {
    if (this.isOpen) this.close();
  }
}
