import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormControl } from '@angular/forms';
import { debounceTime, map, startWith, switchMap } from 'rxjs/operators';
import { FleioApiService } from '@fleio-api/fleio-api.service';
import { Observable, Subscription } from 'rxjs';

@Component({
  selector: 'app-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnInit, OnDestroy {
  filteredObjects$: Observable<any>;
  @Input() control: FormControl | AbstractControl;
  @Input() optionsService: FleioApiService<any>;
  @Input() switchMapHandler: (search: string) => Observable<any>;
  @Input() optionsQueryParams: Record<string, any>;
  @Input() displayClients = false;
  @Input() displayUsers = false;
  @Input() placeholderUnselected = $localize`Select object ...`;
  @Input() placeholderSelected = $localize`Object`;
  @Input() displayFn: (obj: any) => string;
  shownPlaceholder: string;
  valueChangesSubscription: Subscription;
  controlName: string = null;
  loadingEntries = false;

  constructor(
  ) { }

  private setCorrectPlaceholder(newValue) {
    if (this.control?.disabled) {
      this.shownPlaceholder = this.placeholderSelected;
    } else if (newValue && (newValue?.id || newValue?.label)) {
      this.shownPlaceholder = this.placeholderSelected;
    } else {
      this.shownPlaceholder = this.placeholderUnselected;
    }
  }

  setControlName() {
    const formGroup = this.control?.parent?.controls;
    if (formGroup) {
      this.controlName = Object.keys(formGroup).find(name => this.control === formGroup[name]) || null;
    }
  }

  private entriesLoaded() {
    // mark entries as loaded but with a little delay to avoid flickers: this shouldn't affect UX
    setTimeout(() => {
      this.loadingEntries = false;
    }, 200);
  }

  ngOnInit(): void {
    this.shownPlaceholder = this.placeholderUnselected;

    if (this.control) {
      this.setControlName();
      this.setCorrectPlaceholder(this.control.value);

      this.filteredObjects$ = this.control.valueChanges.pipe(
        startWith(''),
        debounceTime(300),
        switchMap(search => {
          this.loadingEntries = true;
          let normalizedSearch: string;
          if (typeof search === 'string') {
            normalizedSearch = search;
          } else {
            normalizedSearch = search?.name || search?.label || search?.id || '';
          }
          if (this.switchMapHandler) {
            const returnValue = this.switchMapHandler(normalizedSearch);
            const entriesSubscription = returnValue.subscribe(() => {
              this.entriesLoaded();
            });
            entriesSubscription.add(() => {
              entriesSubscription.unsubscribe();
            });
            return returnValue;
          }
          const queryParams = {
            search: normalizedSearch,
          };
          if (this.optionsQueryParams) {
            Object.assign(queryParams, this.optionsQueryParams);
          }
          return this.optionsService.list(queryParams).pipe(map(response => {
            this.entriesLoaded();
            return response.objects;
          }));
        }),
      );

      this.valueChangesSubscription = this.control.valueChanges.subscribe(newValue => {
        this.setCorrectPlaceholder(newValue);
      });
    } else {
      console.error('app-autocomplete component needs "control" attribute defined!');
    }
  }

  ngOnDestroy() {
    if (this.valueChangesSubscription) {
      this.valueChangesSubscription.unsubscribe();
      this.valueChangesSubscription = null;
    }
  }

  clearControl() {
    this.control.setValue('');
  }

  defaultDisplayObjectFn(object) {
    if (this.displayFn) {
      return this.displayFn(object);
    }
    if (object) {
      if (typeof object === 'string') {
        return object;
      } else {
        return object?.display || object?.display_name || object?.full_name || object?.name || object?.label || object?.id;
      }
    }
    return '';
  }

}
