import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, forwardRef, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ChipComponent } from '../../data-display/chip/chip.component';
import { faXmark } from '@fortawesome/free-solid-svg-icons';

export type TagInputSize = 'sm' | 'md' | 'lg';
export type TagInputVariant = 'outlined' | 'filled';

@Component({
  selector: 'ui-tag-input',
  standalone: true,
  imports: [CommonModule, ChipComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TagInputComponent),
      multi: true
    }
  ],
  template: `
    <div 
      [class]="containerClasses"
      [attr.aria-label]="ariaLabel"
      [attr.aria-describedby]="ariaDescribedBy"
      (click)="focusInput()">
      
      <div class="ui-tag-input__tags">
        @for (tag of tags; track tag; let i = $index) {
          <div class="ui-tag-input__tag">
            <ui-chip
              [label]="tag"
              [size]="chipSize"
              [variant]="chipVariant"
              [removable]="!disabled && !readonly"
              [closeIcon]="faXmark"
              [disabled]="disabled"
              (chipRemove)="removeTag(i)"
              [attr.aria-label]="'Remove tag: ' + tag">
            </ui-chip>
          </div>
        }
      </div>
      
      @if (!isMaxTagsReached) {
        <input
          #inputElement
          class="ui-tag-input__input"
          type="text"
          [value]="inputValue"
          [placeholder]="effectivePlaceholder"
          [disabled]="disabled"
          [readonly]="readonly"
          [attr.aria-label]="inputAriaLabel"
          [attr.maxlength]="maxTagLength"
          (input)="handleInput($event)"
          (keydown)="handleKeydown($event)"
          (blur)="handleBlur()"
          (focus)="handleFocus()">
      }
    </div>
    
    @if (errorMessage) {
      <div class="ui-tag-input-error" [attr.id]="errorId">
        {{ errorMessage }}
      </div>
    }
    
    @if (helpText && !errorMessage) {
      <div class="ui-tag-input-help" [attr.id]="helpId">
        {{ helpText }}
      </div>
    }
  `,
  styleUrl: './tag-input.component.scss'
})
export class TagInputComponent implements ControlValueAccessor {
  @Input() size: TagInputSize = 'md';
  @Input() variant: TagInputVariant = 'outlined';
  @Input() disabled = false;
  @Input() readonly = false;
  @Input() placeholder = 'Add tags...';
  @Input() maxTags?: number;
  @Input() maxTagLength?: number;
  @Input() allowDuplicates = false;
  @Input() trimTags = true;
  @Input() separators = [',', ';', '|'];
  @Input() ariaLabel = 'Tag input';
  @Input() inputAriaLabel = 'Add new tag';
  @Input() errorMessage?: string;
  @Input() helpText?: string;
  @Input() class = '';
  
  @Output() tagAdd = new EventEmitter<string>();
  @Output() tagRemove = new EventEmitter<{ tag: string; index: number }>();
  @Output() maxTagsReached = new EventEmitter<void>();
  @Output() duplicateTag = new EventEmitter<string>();
  @Output() tagValidationError = new EventEmitter<{ tag: string; error: string }>();
  
  @ViewChild('inputElement') inputElement!: ElementRef<HTMLInputElement>;
  
  // FontAwesome icon
  faXmark = faXmark;
  
  // Internal state
  tags: string[] = [];
  inputValue = '';
  
  // ControlValueAccessor
  private onChange = (value: string[]) => {};
  private onTouched = () => {};
  
  get containerClasses(): string {
    return [
      'ui-tag-input',
      `ui-tag-input--${this.size}`,
      `ui-tag-input--${this.variant}`,
      this.disabled ? 'ui-tag-input--disabled' : '',
      this.errorMessage ? 'ui-tag-input--error' : '',
      this.isMaxTagsReached ? 'ui-tag-input--max-reached' : '',
      this.class
    ].filter(Boolean).join(' ');
  }
  
  get chipSize(): 'sm' | 'md' | 'lg' {
    return this.size;
  }
  
  get chipVariant(): 'filled' | 'outlined' {
    return this.variant === 'outlined' ? 'outlined' : 'filled';
  }
  
  get isMaxTagsReached(): boolean {
    return this.maxTags ? this.tags.length >= this.maxTags : false;
  }
  
  get effectivePlaceholder(): string {
    if (this.tags.length === 0) {
      return this.placeholder;
    }
    if (this.isMaxTagsReached) {
      return `Maximum ${this.maxTags} tags reached`;
    }
    return '';
  }
  
  get errorId(): string {
    return `ui-tag-input-error-${Math.random().toString(36).substr(2, 9)}`;
  }
  
  get helpId(): string {
    return `ui-tag-input-help-${Math.random().toString(36).substr(2, 9)}`;
  }
  
  get ariaDescribedBy(): string {
    const ids: string[] = [];
    if (this.errorMessage) {
      ids.push(this.errorId);
    } else if (this.helpText) {
      ids.push(this.helpId);
    }
    return ids.join(' ') || '';
  }
  
  // ControlValueAccessor implementation
  writeValue(value: string[]): void {
    this.tags = Array.isArray(value) ? [...value] : [];
  }
  
  registerOnChange(fn: (value: string[]) => void): void {
    this.onChange = fn;
  }
  
  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }
  
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
  
  // Event handlers
  handleInput(event: Event): void {
    const target = event.target as HTMLInputElement;
    this.inputValue = target.value;
    
    // Check for separators
    const lastChar = this.inputValue[this.inputValue.length - 1];
    if (this.separators.includes(lastChar)) {
      this.addTagFromInput();
    }
  }
  
  handleKeydown(event: KeyboardEvent): void {
    switch (event.key) {
      case 'Enter':
      case 'Tab':
        if (this.inputValue.trim()) {
          event.preventDefault();
          this.addTagFromInput();
        }
        break;
        
      case 'Backspace':
        if (!this.inputValue && this.tags.length > 0) {
          event.preventDefault();
          this.removeTag(this.tags.length - 1);
        }
        break;
        
      case 'Escape':
        this.inputValue = '';
        if (this.inputElement) {
          this.inputElement.nativeElement.value = '';
        }
        break;
    }
  }
  
  handleBlur(): void {
    this.onTouched();
    if (this.inputValue.trim()) {
      this.addTagFromInput();
    }
  }
  
  handleFocus(): void {
    // Focus handling if needed
  }
  
  focusInput(): void {
    if (!this.disabled && !this.readonly && !this.isMaxTagsReached && this.inputElement) {
      this.inputElement.nativeElement.focus();
    }
  }
  
  // Tag management
  addTag(tag: string): boolean {
    const processedTag = this.trimTags ? tag.trim() : tag;
    
    if (!processedTag) {
      return false;
    }
    
    // Check max length
    if (this.maxTagLength && processedTag.length > this.maxTagLength) {
      this.tagValidationError.emit({
        tag: processedTag,
        error: `Tag exceeds maximum length of ${this.maxTagLength} characters`
      });
      return false;
    }
    
    // Check max tags
    if (this.isMaxTagsReached) {
      this.maxTagsReached.emit();
      return false;
    }
    
    // Check duplicates
    if (!this.allowDuplicates && this.tags.includes(processedTag)) {
      this.duplicateTag.emit(processedTag);
      return false;
    }
    
    // Add tag
    this.tags = [...this.tags, processedTag];
    this.onChange(this.tags);
    this.tagAdd.emit(processedTag);
    
    return true;
  }
  
  removeTag(index: number): void {
    if (index >= 0 && index < this.tags.length) {
      const removedTag = this.tags[index];
      this.tags = this.tags.filter((_, i) => i !== index);
      this.onChange(this.tags);
      this.tagRemove.emit({ tag: removedTag, index });
      
      // Focus input after removal
      setTimeout(() => this.focusInput(), 0);
    }
  }
  
  private addTagFromInput(): void {
    if (this.inputValue) {
      // Remove separator characters
      let cleanValue = this.inputValue;
      this.separators.forEach(sep => {
        cleanValue = cleanValue.replace(new RegExp(`\\${sep}`, 'g'), '');
      });
      
      if (this.addTag(cleanValue)) {
        this.inputValue = '';
        if (this.inputElement) {
          this.inputElement.nativeElement.value = '';
        }
      }
    }
  }
  
  // Public methods
  clear(): void {
    this.tags = [];
    this.inputValue = '';
    if (this.inputElement) {
      this.inputElement.nativeElement.value = '';
    }
    this.onChange(this.tags);
  }
  
  addTagProgrammatically(tag: string): boolean {
    return this.addTag(tag);
  }
  
  getTagsAsString(separator = ', '): string {
    return this.tags.join(separator);
  }
}