import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, forwardRef, signal, ViewChild, ElementRef, HostListener } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faTimes } from '@fortawesome/free-solid-svg-icons';

export type AutocompleteSize = 'sm' | 'md' | 'lg';
export type AutocompleteVariant = 'outlined' | 'filled';

export interface AutocompleteOption {
  value: any;
  label: string;
  secondaryText?: string;
  disabled?: boolean;
}

@Component({
  selector: 'ui-autocomplete',
  standalone: true,
  imports: [CommonModule, FormsModule, FontAwesomeModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => AutocompleteComponent),
      multi: true
    }
  ],
  template: `
    <div 
      class="ui-autocomplete"
      [class.ui-autocomplete--sm]="size === 'sm'"
      [class.ui-autocomplete--md]="size === 'md'"
      [class.ui-autocomplete--lg]="size === 'lg'"
      [class.ui-autocomplete--outlined]="variant === 'outlined'"
      [class.ui-autocomplete--filled]="variant === 'filled'"
      [class.ui-autocomplete--focused]="isFocused()"
      [class.ui-autocomplete--error]="hasError"
      [class.ui-autocomplete--disabled]="disabled"
      [class.ui-autocomplete--loading]="loading"
      [class.ui-autocomplete--full-width]="fullWidth"
    >
      @if (label) {
        <label 
          class="ui-autocomplete__label"
          [class.ui-autocomplete__label--required]="required"
          [for]="autocompleteId"
        >
          {{ label }}
        </label>
      }
      
      <div class="ui-autocomplete__input-wrapper">
        <input
          #inputElement
          type="text"
          class="ui-autocomplete__input"
          [id]="autocompleteId"
          [value]="displayValue()"
          [placeholder]="placeholder"
          [disabled]="disabled"
          [readonly]="readonly"
          [required]="required"
          [attr.aria-label]="ariaLabel || placeholder || 'Autocomplete'"
          [attr.aria-expanded]="isDropdownOpen()"
          [attr.aria-haspopup]="true"
          [attr.aria-autocomplete]="'list'"
          [attr.aria-controls]="autocompleteId + '-listbox'"
          [attr.aria-activedescendant]="highlightedIndex() >= 0 ? autocompleteId + '-option-' + highlightedIndex() : null"
          [attr.aria-describedby]="getAriaDescribedBy()"
          [attr.aria-invalid]="hasError"
          (input)="onInput($event)"
          (focus)="onFocus()"
          (blur)="onBlur()"
          (keydown)="onKeyDown($event)"
          autocomplete="off"
        />
        
        @if (displayValue() && clearable && !disabled && !readonly) {
          <button
            type="button"
            class="ui-autocomplete__clear-button"
            [attr.aria-label]="'Clear input'"
            (click)="clearValue()"
            (mousedown)="$event.preventDefault()"
          >
            <fa-icon [icon]="faTimes" size="sm"></fa-icon>
          </button>
        }
      </div>
      
      @if (isDropdownOpen()) {
        <div 
          class="ui-autocomplete__dropdown"
          [class.ui-autocomplete__dropdown--sm]="size === 'sm'"
          [class.ui-autocomplete__dropdown--lg]="size === 'lg'"
          [id]="autocompleteId + '-listbox'"
          role="listbox"
          [attr.aria-label]="'Options'"
        >
          @if (loading) {
            <div class="ui-autocomplete__loading">
              Loading...
            </div>
          } @else if (filteredOptions().length === 0) {
            <div class="ui-autocomplete__no-options">
              {{ noOptionsText }}
            </div>
          } @else {
            @for (option of filteredOptions(); track option.value; let index = $index) {
              <button
                type="button"
                class="ui-autocomplete__option"
                [class.ui-autocomplete__option--sm]="size === 'sm'"
                [class.ui-autocomplete__option--md]="size === 'md'"
                [class.ui-autocomplete__option--lg]="size === 'lg'"
                [class.ui-autocomplete__option--highlighted]="highlightedIndex() === index"
                [class.ui-autocomplete__option--selected]="isSelected(option)"
                [class.ui-autocomplete__option--disabled]="option.disabled"
                [id]="autocompleteId + '-option-' + index"
                [disabled]="option.disabled"
                [attr.aria-selected]="isSelected(option)"
                role="option"
                (click)="selectOption(option)"
                (mouseenter)="setHighlightedIndex(index)"
                (mousedown)="$event.preventDefault()"
              >
                <span class="ui-autocomplete__option-text">{{ option.label }}</span>
                @if (option.secondaryText) {
                  <span class="ui-autocomplete__option-secondary">{{ option.secondaryText }}</span>
                }
              </button>
            }
          }
        </div>
      }
      
      @if (helperText && !hasError) {
        <div class="ui-autocomplete__helper-text" [id]="autocompleteId + '-helper'">
          {{ helperText }}
        </div>
      }
      
      @if (errorText && hasError) {
        <div class="ui-autocomplete__error-text" [id]="autocompleteId + '-error'">
          {{ errorText }}
        </div>
      }
    </div>
  `,
  styleUrl: './autocomplete.component.scss'
})
export class AutocompleteComponent implements ControlValueAccessor {
  @ViewChild('inputElement', { static: true }) inputElement!: ElementRef<HTMLInputElement>;

  // Core inputs
  @Input() label: string = '';
  @Input() placeholder: string = 'Search...';
  @Input() helperText: string = '';
  @Input() errorText: string = '';
  @Input() size: AutocompleteSize = 'md';
  @Input() variant: AutocompleteVariant = 'outlined';
  
  // Options and filtering
  @Input() options: AutocompleteOption[] = [];
  @Input() filterFn?: (option: AutocompleteOption, query: string) => boolean;
  @Input() minQueryLength: number = 0;
  @Input() maxOptions: number = 100;
  @Input() noOptionsText: string = 'No options found';
  
  // Behavior
  @Input() disabled: boolean = false;
  @Input() readonly: boolean = false;
  @Input() required: boolean = false;
  @Input() clearable: boolean = true;
  @Input() loading: boolean = false;
  @Input() fullWidth: boolean = false;
  @Input() openOnFocus: boolean = false;
  @Input() closeOnSelect: boolean = true;
  
  // Accessibility
  @Input() ariaLabel: string = '';
  @Input() autocompleteId: string = `ui-autocomplete-${Math.random().toString(36).substr(2, 9)}`;

  // Outputs
  @Output() valueChange = new EventEmitter<any>();
  @Output() optionSelected = new EventEmitter<AutocompleteOption>();
  @Output() queryChange = new EventEmitter<string>();
  @Output() focused = new EventEmitter<FocusEvent>();
  @Output() blurred = new EventEmitter<FocusEvent>();
  @Output() dropdownOpen = new EventEmitter<void>();
  @Output() dropdownClose = new EventEmitter<void>();

  // Font Awesome icons
  protected readonly faTimes = faTimes;

  // Internal state
  private _selectedOption = signal<AutocompleteOption | null>(null);
  private _query = signal<string>('');
  private _displayValue = signal<string>('');
  private _isFocused = signal<boolean>(false);
  private _isDropdownOpen = signal<boolean>(false);
  private _highlightedIndex = signal<number>(-1);
  private _filteredOptions = signal<AutocompleteOption[]>([]);

  // Public signals
  selectedOption = this._selectedOption.asReadonly();
  query = this._query.asReadonly();
  displayValue = this._displayValue.asReadonly();
  isFocused = this._isFocused.asReadonly();
  isDropdownOpen = this._isDropdownOpen.asReadonly();
  highlightedIndex = this._highlightedIndex.asReadonly();
  filteredOptions = this._filteredOptions.asReadonly();

  // ControlValueAccessor implementation
  private onChange = (value: any) => {};
  private onTouched = () => {};

  get hasError(): boolean {
    return !!this.errorText;
  }

  ngOnInit(): void {
    this.updateFilteredOptions();
  }

  ngOnChanges(): void {
    this.updateFilteredOptions();
  }

  // ControlValueAccessor methods
  writeValue(value: any): void {
    if (value) {
      const option = this.options.find(opt => opt.value === value);
      if (option) {
        this._selectedOption.set(option);
        this._displayValue.set(option.label);
        this._query.set(option.label);
      } else {
        // If value doesn't match any option, treat as free text
        this._selectedOption.set(null);
        this._displayValue.set(String(value));
        this._query.set(String(value));
      }
    } else {
      this.clearValue();
    }
    this.updateFilteredOptions();
  }

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

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

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

  // Event handlers
  onInput(event: Event): void {
    if (this.readonly) return;

    const target = event.target as HTMLInputElement;
    const value = target.value;
    
    this._query.set(value);
    this._displayValue.set(value);
    this._selectedOption.set(null);
    this._highlightedIndex.set(-1);
    
    this.updateFilteredOptions();
    this.openDropdown();
    
    this.onChange(value);
    this.valueChange.emit(value);
    this.queryChange.emit(value);
  }

  onFocus(): void {
    this._isFocused.set(true);
    this.focused.emit();
    
    if (this.openOnFocus) {
      this.openDropdown();
    }
  }

  onBlur(): void {
    // Delay to allow for option selection
    setTimeout(() => {
      this._isFocused.set(false);
      this.closeDropdown();
      this.onTouched();
      this.blurred.emit();
    }, 150);
  }

  onKeyDown(event: KeyboardEvent): void {
    if (this.readonly) return;

    switch (event.key) {
      case 'ArrowDown':
        event.preventDefault();
        this.highlightNext();
        this.openDropdown();
        break;
        
      case 'ArrowUp':
        event.preventDefault();
        this.highlightPrevious();
        this.openDropdown();
        break;
        
      case 'Enter':
        event.preventDefault();
        if (this.isDropdownOpen() && this.highlightedIndex() >= 0) {
          const option = this.filteredOptions()[this.highlightedIndex()];
          if (option && !option.disabled) {
            this.selectOption(option);
          }
        }
        break;
        
      case 'Escape':
        this.closeDropdown();
        this.inputElement.nativeElement.blur();
        break;
        
      case 'Tab':
        this.closeDropdown();
        break;
    }
  }

  // Option selection
  selectOption(option: AutocompleteOption): void {
    if (option.disabled) return;

    this._selectedOption.set(option);
    this._displayValue.set(option.label);
    this._query.set(option.label);
    this._highlightedIndex.set(-1);

    if (this.closeOnSelect) {
      this.closeDropdown();
    }

    this.onChange(option.value);
    this.valueChange.emit(option.value);
    this.optionSelected.emit(option);

    // Return focus to input
    this.inputElement.nativeElement.focus();
  }

  clearValue(): void {
    this._selectedOption.set(null);
    this._displayValue.set('');
    this._query.set('');
    this._highlightedIndex.set(-1);
    
    this.updateFilteredOptions();
    this.closeDropdown();
    
    this.onChange(null);
    this.valueChange.emit(null);
    this.queryChange.emit('');

    if (this.inputElement) {
      this.inputElement.nativeElement.focus();
    }
  }

  // Dropdown management
  openDropdown(): void {
    if (this.disabled || this.readonly) return;
    
    if (this.query().length >= this.minQueryLength || this.filteredOptions().length > 0) {
      this._isDropdownOpen.set(true);
      this.dropdownOpen.emit();
    }
  }

  closeDropdown(): void {
    this._isDropdownOpen.set(false);
    this._highlightedIndex.set(-1);
    this.dropdownClose.emit();
  }

  // Navigation
  highlightNext(): void {
    const options = this.filteredOptions();
    if (options.length === 0) return;

    const currentIndex = this.highlightedIndex();
    let nextIndex = currentIndex + 1;

    // Skip disabled options
    while (nextIndex < options.length && options[nextIndex].disabled) {
      nextIndex++;
    }

    if (nextIndex >= options.length) {
      // Wrap to first enabled option
      nextIndex = 0;
      while (nextIndex < options.length && options[nextIndex].disabled) {
        nextIndex++;
      }
    }

    if (nextIndex < options.length) {
      this._highlightedIndex.set(nextIndex);
    }
  }

  highlightPrevious(): void {
    const options = this.filteredOptions();
    if (options.length === 0) return;

    const currentIndex = this.highlightedIndex();
    let prevIndex = currentIndex - 1;

    // Skip disabled options
    while (prevIndex >= 0 && options[prevIndex].disabled) {
      prevIndex--;
    }

    if (prevIndex < 0) {
      // Wrap to last enabled option
      prevIndex = options.length - 1;
      while (prevIndex >= 0 && options[prevIndex].disabled) {
        prevIndex--;
      }
    }

    if (prevIndex >= 0) {
      this._highlightedIndex.set(prevIndex);
    }
  }

  setHighlightedIndex(index: number): void {
    this._highlightedIndex.set(index);
  }

  // Utility methods
  isSelected(option: AutocompleteOption): boolean {
    const selected = this.selectedOption();
    return selected ? selected.value === option.value : false;
  }

  getAriaDescribedBy(): string | null {
    const ids: string[] = [];
    
    if (this.helperText && !this.hasError) {
      ids.push(this.autocompleteId + '-helper');
    }
    
    if (this.errorText && this.hasError) {
      ids.push(this.autocompleteId + '-error');
    }
    
    return ids.length > 0 ? ids.join(' ') : null;
  }

  private updateFilteredOptions(): void {
    const query = this.query();
    const options = this.options;

    if (query.length < this.minQueryLength) {
      this._filteredOptions.set([]);
      return;
    }

    let filtered: AutocompleteOption[];

    if (this.filterFn) {
      filtered = options.filter(option => this.filterFn!(option, query));
    } else {
      // Default filtering: case-insensitive substring match on label
      const lowerQuery = query.toLowerCase();
      filtered = options.filter(option => 
        option.label.toLowerCase().includes(lowerQuery) ||
        (option.secondaryText && option.secondaryText.toLowerCase().includes(lowerQuery))
      );
    }

    // Limit results
    if (filtered.length > this.maxOptions) {
      filtered = filtered.slice(0, this.maxOptions);
    }

    this._filteredOptions.set(filtered);
    
    // Reset highlighted index if out of bounds
    if (this.highlightedIndex() >= filtered.length) {
      this._highlightedIndex.set(-1);
    }
  }

  // Close dropdown when clicking outside
  @HostListener('document:click', ['$event'])
  onDocumentClick(event: Event): void {
    const target = event.target as Element;
    if (!target.closest('.ui-autocomplete')) {
      this.closeDropdown();
    }
  }
}