import {
  Component,
  ChangeDetectionStrategy,
  Output,
  EventEmitter,
  Input,
  OnDestroy,
  AfterViewInit,
  ElementRef,
  ViewChild,
  ContentChild,
  ChangeDetectorRef,
  HostBinding,
  AfterContentInit,
  HostListener,
  forwardRef,
  Optional,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UiAutocompleteComponent } from '@ui/autocomplete';
import { UiFormControlDirective } from '@ui/form-control';
import { containsElement } from '@utils/common';
import { Subscription } from 'rxjs';
import { ScanAdapterService } from '@adapter/scan';

// TODO: @ui/searchbox mit dieser implementierung ersetzen

@Component({
  selector: 'ui-searchbox',
  templateUrl: 'searchbox.component.html',
  styleUrls: ['searchbox.component.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => UiSearchboxNextComponent), multi: true }],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UiSearchboxNextComponent extends UiFormControlDirective<any>
  implements AfterViewInit, OnDestroy, AfterContentInit, ControlValueAccessor {
  disabled: boolean;
  type = 'text';

  @ViewChild('input', { read: ElementRef, static: true })
  input: ElementRef;

  @ContentChild(UiAutocompleteComponent)
  autocomplete: UiAutocompleteComponent;

  @Input()
  focusAfterViewInit: boolean = true;

  @Input()
  placeholder: string = '';

  @Input()
  query: string = '';

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

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

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

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

  @Input()
  loading = false;

  @Input()
  scanner = false;

  @Input()
  hint: string = '';

  @Input()
  autocompleteValueSelector: (item: any) => string = (item: any) => item;

  get valueEmpty(): boolean {
    return !!this.query;
  }

  clear(): void {
    this.setQuery('');
  }

  @HostBinding('class.autocomplete-opend')
  get autocompleteOpen() {
    return this.autocomplete?.opend;
  }

  get canScan() {
    return !this.query && this.scanner && this.scanAdapterService?.isReady();
  }

  get canClear() {
    return !!this.query;
  }

  get showHint() {
    return !!this.hint;
  }

  subscriptions = new Subscription();

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

  onTouched = () => {};

  constructor(
    private cdr: ChangeDetectorRef,
    private elementRef: ElementRef<HTMLElement>,
    @Optional() private scanAdapterService: ScanAdapterService
  ) {
    super();
  }

  writeValue(obj: any): void {
    this.setQuery(obj, false);
  }

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

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

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

  ngAfterViewInit() {
    if (this.focusAfterViewInit) {
      this.focus();
    }
  }

  ngAfterContentInit() {
    if (this.autocomplete) {
      this.subscriptions.add(
        this.autocomplete?.selectItem.subscribe((item) => {
          this.setQuery(item);
          this.search.emit(item);
          this.autocomplete?.close();
        })
      );
    }
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }

  startScan() {
    if (!!this.scanAdapterService?.isReady()) {
      this.subscriptions.add(
        this.scanAdapterService.scan().subscribe((result) => {
          this.scan.emit(result);
          this.setQuery(result);
          this.autocomplete?.close();
        })
      );
    }
  }

  setQuery(query: string, emitEvent: boolean = true, complete?: boolean) {
    this.query = query;
    if (emitEvent) {
      this.queryChange.emit(query);
      this.onChange(query);
      this.onTouched();
    }
    if (complete) {
      this.complete.emit(query);
    }
    this.cdr.markForCheck();
  }

  focus() {
    this.input?.nativeElement?.focus();
  }

  clearHint() {
    this.hint = '';
    this.focused.emit(true);
    this.cdr.markForCheck();
  }

  onKeyup(event: KeyboardEvent) {
    if (event.key === 'Enter') {
      if (this.autocomplete?.opend && this.autocomplete?.activeItem) {
        this.setQuery(this.autocomplete?.activeItem?.item);
        this.autocomplete?.close();
      }
      this.search.emit(this.query);

      event.preventDefault();
    } else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
      this.handleArrowUpDownEvent(event);
    }
  }

  handleArrowUpDownEvent(event: KeyboardEvent) {
    this.autocomplete?.handleKeyboardEvent(event);
    if (this.autocomplete?.activeItem) {
      const query = this.autocompleteValueSelector(this.autocomplete.activeItem.item);
      this.setQuery(query, false);
    }
  }

  @HostListener('window:click', ['$event'])
  focusLost(event: MouseEvent) {
    if (this.autocomplete?.opend && !containsElement(this.elementRef.nativeElement, event.target as Element)) {
      this.autocomplete?.close();
    }
  }

  @HostListener('focusout', ['$event'])
  onBlur() {
    this.onTouched();
    this.focused.emit(false);
    this.cdr.markForCheck();
  }
}
