import {
  Component,
  ChangeDetectionStrategy,
  Input,
  Optional,
  ViewChild,
  AfterViewInit,
  OnInit,
  Inject,
  OnDestroy,
  ChangeDetectorRef,
  Output,
  EventEmitter,
} from '@angular/core';
import { UiAutocompleteComponent } from '@ui/autocomplete';
import { Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, switchMap, tap } from 'rxjs/operators';
import { UiFilterAutocomplete, UiFilterAutocompleteProvider } from '../../providers';
import { IUiInputGroup, UiInput, UiInputGroup } from '../../tree';

@Component({
  selector: 'ui-filter-input-group-main',
  templateUrl: 'filter-input-group-main.component.html',
  styleUrls: ['filter-input-group-main.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UiFilterInputGroupMainComponent implements OnInit, OnDestroy, AfterViewInit {
  @ViewChild(UiAutocompleteComponent, { read: UiAutocompleteComponent, static: false })
  autocompleteComponent: UiAutocompleteComponent;

  @Output()
  queryChange = new EventEmitter<string>();

  @Output()
  search = new EventEmitter<string>();

  @Input()
  loading: boolean;

  @Input()
  hint: string;

  @Input()
  showDescription: boolean = true;

  @Input()
  scanner = false;

  private _inputGroup: UiInputGroup;

  @Input()
  set inputGroup(value: IUiInputGroup) {
    if (value instanceof UiInputGroup) {
      this._inputGroup = value;
    } else {
      this._inputGroup = UiInputGroup.create(value);
    }
    this.subscribeChanges();
    this.initAutocomplete();
  }

  get uiInput(): UiInput {
    return this._inputGroup?.input?.find((f) => f);
  }

  private _autocompleteProvider: UiFilterAutocompleteProvider;
  get autocompleteProvider() {
    return this._autocompleteProvider;
  }

  autocompleteResults$: Observable<UiFilterAutocomplete[]>;

  complete = new Subject<string>();

  private changeSubscriptions: Subscription;

  constructor(
    @Inject(UiFilterAutocompleteProvider) @Optional() private autocompleteProviders: UiFilterAutocompleteProvider[],
    private cdr: ChangeDetectorRef
  ) {}

  ngOnInit() {
    this._autocompleteProvider = this.autocompleteProviders?.find((provider) => !!provider);
  }

  onQueryChange(query: string) {
    this.uiInput?.setValue(query);
    this.queryChange.emit(query);
  }

  ngAfterViewInit() {
    this.initAutocomplete();
  }

  ngOnDestroy() {
    this.unsubscribeChanges();
  }

  subscribeChanges() {
    this.unsubscribeChanges();
    const sub = this.uiInput?.changes?.pipe(filter((changes) => changes?.keys.includes('value'))).subscribe(() => {
      this.cdr.markForCheck();
    });
    if (sub) {
      this.changeSubscriptions.add(sub);
    }
  }

  unsubscribeChanges() {
    this.changeSubscriptions?.unsubscribe();
    this.changeSubscriptions = new Subscription();
  }

  initAutocomplete() {
    this.autocompleteResults$ = this.complete.asObservable().pipe(
      debounceTime(this._debounceTimeAutocompleteMilliseconds()),
      distinctUntilChanged(),
      switchMap(() => this.autocompleteProvider.complete(this.uiInput)),
      tap((complete) => {
        if (complete.length > 0) {
          this.autocompleteComponent.open();
        } else {
          this.autocompleteComponent.close();
        }
      })
    );
  }

  setAutocompleteProvider(provider: UiFilterAutocompleteProvider) {
    this._autocompleteProvider = provider;
  }

  private _debounceTimeAutocompleteMilliseconds(): number {
    if (!this.autocompleteProvider) {
      return;
    }

    let debounceTimeMilliseconds: number;
    switch (this.autocompleteProvider.for) {
      case 'catalog':
        debounceTimeMilliseconds = 250;
        break;
      case 'goods-in':
        debounceTimeMilliseconds = 300;
        break;
      case 'goods-out':
        debounceTimeMilliseconds = 300;
        break;
      default:
        debounceTimeMilliseconds = 300;
        break;
    }
    return debounceTimeMilliseconds;
  }

  emitSearch(query: string) {
    setTimeout(() => {
      this.search.emit(query);
    }, 1);
  }
}
