import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FilteringService } from '@shared/ui-api/filtering.service';
import { IFilterOption } from '@shared/ui-api/interfaces/route-config/filter-option';
import { UntypedFormControl } from '@angular/forms';
import { debounceTime, startWith, switchMap } from 'rxjs/operators';
import { FleioApiService } from '@fleio-api/fleio-api.service';
import { IBaseFleioObjectModel } from '@fleio-api/base-model/base-fleio-object.model';
import { FleioObjectsList } from '@fleio-api/fleio-objects-list';
import { NotificationService } from '@shared/ui-api/notification.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-objects-list-filtering-custom-model',
  templateUrl: './objects-list-filtering-custom-model.component.html',
  styleUrls: ['./objects-list-filtering-custom-model.component.scss']
})
export class ObjectsListFilteringCustomModelComponent implements OnInit, OnDestroy {
  @Input() filterOption: IFilterOption;
  myControl = new UntypedFormControl();
  selectedItem?: { id: string };
  filteredItems = [];
  loadingItems = false;
  servicesMap: Record<string, unknown>;
  exclude = false;
  hasMore = false;
  pageNo: number;
  placeholder = $localize`Search ...`;
  filterOptionsSubscription: Subscription;

  constructor(
    private filterService: FilteringService, private notificationService: NotificationService,
  ) {
    this.servicesMap = filterService.itemsServices;
  }

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

  applyFiltering() {
    if (!this.selectedItem) {
      return this.notificationService.showMessage($localize`Please enter a valid value`);
    }
    let filteringField = this.filterOption.field;
    let filteringValue = this.selectedItem.id;
    if (filteringValue === null) {
      filteringValue = 'null';
    }
    if (this.exclude) {
      filteringField = filteringField + '__ne';
    }
    this.filterService.addFilter(filteringField, filteringValue, this.filterOption?.resetQueryParams);
  }

  displayFn(item: { id: string | null; name?: string; display?: string; }) {
    if (!item) {
      return '';
    }
    if (item.id === null) {
      return $localize`Filter items without related record`;
    }

    const displayField = this.filterOption ? item[this.filterOption.itemsDisplayField] : null;
    return displayField || item.name || item.id || '';
  }

  onItemSelection(item) {
    this.selectedItem = item;
  }

  private getItems(value: string): any {
    if (value) {
      this.placeholder = $localize`Filter value`;
    } else {
      this.placeholder = $localize`Search ...`;
    }
    this.pageNo = 1;
    if (this.filterOption) {
      const service = this.servicesMap[this.filterOption.items] as FleioApiService<IBaseFleioObjectModel>;
      if (!this.filterOption.itemsFilter) {
        return service.list({search: value});
      } else {
        let filtering = '';
        const fieldsKeys = Object.keys(this.filterOption.itemsFilter);
        fieldsKeys.forEach((key, index) => {
          filtering += `${key}:${this.filterOption.itemsFilter[key]}`;
          if (index < fieldsKeys.length - 1) {
            filtering += '+';
          }
        });
        return service.list({
          search: value,
          filtering,
        });
      }
    }
    return [];
  }

  ngOnInit() {
    this.selectedItem = null;

    this.filterOptionsSubscription = this.myControl.valueChanges
      .pipe(
        startWith(''),
        debounceTime(300),
        switchMap(value => {
          this.loadingItems = true;
          return this.getItems(value);
        })
      )
      .subscribe((objectList: FleioObjectsList<IBaseFleioObjectModel>) => {
        setTimeout(() => {
          this.loadingItems = false;
        }, 200);
        this.filteredItems = objectList.objects;
        this.hasMore = !!objectList.next;
        if (this.filterOption.itemsWithoutRelatedRecord !== false) {
          // default is undefined and we treat it as true
          this.filteredItems.unshift({id: null});
        }
      });
  }

  loadMore() {
    this.pageNo += 1;
    const service = this.servicesMap[this.filterOption.items] as FleioApiService<IBaseFleioObjectModel>;

    service.list({
      search: this.myControl.value || '',
      page: this.pageNo,
    }).subscribe(objectList => {
      for (const obj of objectList.objects) {
        this.filteredItems.push(obj);
      }
      this.hasMore = !!objectList.next;
    });
  }

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