import {Component, ElementRef, forwardRef, Input, ViewChild} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Observable, of} from 'rxjs';
import {catchError, debounceTime, distinctUntilChanged, switchMap, tap} from 'rxjs/operators';
import {TypeaheadService} from '../../services/typeahead.service';
import {NgbTypeahead} from '@ng-bootstrap/ng-bootstrap';

@Component({
    selector: 'fm-tag-input',
    templateUrl: 'tag-input.component.html',
    styleUrls: ['tag-input.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => TagInputComponent),
            multi: true
        }
    ],
    standalone: false
})

export class TagInputComponent implements ControlValueAccessor {
  @Input() tags: string[] = [];
  @ViewChild('tagInputElement', {static: true}) tagInputElement: ElementRef;
  @ViewChild(NgbTypeahead, {static: true}) typeahead: NgbTypeahead;

  public tag: string;
  searching = false;
  searchFailed = false;

  constructor(private typeaheadService: TypeaheadService) {
  }

  tagExists(tag) {
    if (tag.length === 0) {
      return true;
    }
    for (const t of this.tags) {
      if (t.toLowerCase() === tag.toLowerCase()) {
        return true;
      }
    }
    return false;
  }

  addTag(tag, keepFocus = true) {
    if (!this.tagExists(tag)) {
      this.tags.push(tag);
      this.propagateChange(this.tags);
    }
    this.tag = '';

    if (keepFocus) {
      this.tagInputElement.nativeElement.focus();
    }
  }

  handleDeleteTag(tag) {
    const tags = [];
    for (const t of this.tags) {
      if (t !== tag) {
        tags.push(t);
      }
    }
    this.tags = tags;
    this.propagateChange(tags);
  }

  handleBlur(event, keepFocus = true) {
    if (!this.typeahead.isPopupOpen()) {
      this.addTag(this.tag, keepFocus);
    } else {
      this.tag = '';
    }
  }

  handleKeyUp(event: KeyboardEvent) {
    if (event.keyCode === 188) {
      const tag = this.tag.substr(0, this.tag.length - 1); // strip ,
      this.addTag(tag);
    }
  }

  handleSelect(event) {
    if (!this.tagExists(event.item)) {
      this.tags.push(event.item);
      this.propagateChange(this.tags);
    }
    event.preventDefault();
    this.tag = '';
  }

  propagateChange = (_: any) => {
  };

  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  findTag = (text$: Observable<string>) =>
    text$.pipe(
      distinctUntilChanged(),
      debounceTime(200),
      tap(() => this.searching = true),
      switchMap(term => term.length < 1 ? of([]) :
        this.typeaheadService.getTagTypeaheadItems(term).pipe(
          tap(() => this.searchFailed = false),
          catchError(() => {
            this.searchFailed = true;
            return of([]);
          }))
      ),
      tap(() => this.searching = false)
    );

  writeValue(value: any) {
    this.tags = value;
    this.tag = '';
  }

  registerOnTouched() {
  }
}
