import { IFilterParams } from 'ag-grid-community';
import BaseFilter from './base-filter';
import { observable } from 'aurelia-binding';
import { EnumerationTypeCategory } from '../../utils/enumeration-types-helper';

type EnumerationTypeCategorySelected = EnumerationTypeCategory & { selected?: boolean, hide?: boolean };

export class EnumerationCategoryFilter extends BaseFilter {

  private filterValues: Array<EnumerationTypeCategorySelected>;

  private categories: Array<EnumerationTypeCategory>;

  @observable
  private searchText: string;

  public selectedIds: Array<number> = [];
  private selectedIdInternalChange: boolean = false;

  public init(params: IFilterParams & { categories: Array<EnumerationTypeCategory> }): void {
    if (params.categories != null) {
      // TODO sort by name
      this.categories = params.categories;
    } else {
      throw new Error("Categories parameter is missing");
    }
    this.filterChangedCallback = params.filterChangedCallback;
    super.init(params);
  }

  public isFilterActive(): boolean {
    return this.filterValues?.some(x => x.selected);
  }

  public async onOpenDropDown() {
    if (this.isDestroyed()) {
      // Destroyed
      return;
    }
    this.dispose();
    let oldFilterValues = this.filterValues;
    this.getCategories();
    if (oldFilterValues && oldFilterValues.length > 0) {
      oldFilterValues.forEach(oldFilterValue => {
        if (this.filterValues && this.filterValues.some(x => x.category == oldFilterValue.category))
          this.filterValues.find(x => x.category == oldFilterValue.category).selected = oldFilterValue.selected;
      });
      this.selectedIds.splice(0);
      this.selectedIds.push(...this.filterValues.filter(x => x.selected).map(x => x.category));
    }
    else {
      this.resetFilter();
    }
  }

  public async setupContentGui(): Promise<string> {
    await this.onOpenDropDown();
    return `     
      <div class="floating-filter__body__enum">
        <div class="floating-filter__body__search">
          <ui-field t="[label]common:grid.searchShort" >
            <ui-input type="text" value.bind="searchText & debounce:500" clear t="[placeholder]common:grid.searchShort">
              <ui-input-addon icon="digi-search"></ui-input-addon>
            </ui-input>
          </ui-field>
        </div>

        <div class="floating-filter__body__enum__list">
          <div repeat.for="filterValue of filterValues" if.bind="!filterValue.hide" class="floating-filter__body__enum__list__elem">
            <ui-checkbox checked.bind="filterValue.selected" t="[title]\${filterValue.name}">
              \${filterValue.name | t}
            </ui-checkbox>
          </div>
        </div>
      </div>`;
  }

  public getCategories(existing: Array<number> = null) {
    this.filterValues = new Array<EnumerationTypeCategorySelected>();
    this.dispose();

    this.categories.forEach(category => {
      let filterValue: EnumerationTypeCategorySelected = Object.assign({}, category, { hide: false });
      filterValue.selected = existing?.find(x => x == filterValue.category) != null;

      this.filterValues.push(filterValue);
      let disposable = this.bindingEngine.propertyObserver(filterValue, 'selected').subscribe(this.filterValuesChanged.bind(this));
      this.disposables.push(disposable);
    });

    this.disposables.push(this.bindingEngine.collectionObserver(this.selectedIds).subscribe((changeRecords) => {
      if (!this.selectedIdInternalChange) {
        if (changeRecords?.length > 0 && changeRecords[0].removed.length > 0) {
          changeRecords[0].removed.forEach(id => {
            this.filterValues.find(x => x.category == id).selected = false;
          });
        }
        this.selectedIds.forEach(id => {
          this.filterValues.find(x => x.category == id).selected = true;
        })
      }
    }));
  }

  public resetFilter(): void {
    this.filterValues?.forEach(filterValue => filterValue.selected = false);
  }

  public removeFilterValue(filterValue: EnumerationTypeCategorySelected): void {
    filterValue.selected = false;
  }

  public filterValuesChanged(newVal: boolean, oldVal: boolean) {
    if (newVal != oldVal) {
      this.setSelectedValues();
      const instance = this.params.api.getFilterInstance((this.params as IFilterParams).colDef.field);
      instance.setModel({ filterType: 'set', values: this.selectedIds });
    }
  }

  private setSelectedValues() {
    this.selectedIdInternalChange = true;
    this.selectedIds.splice(0);
    this.selectedIds.push(...this.filterValues.filter(x => x.selected).map(x => x.category));
    this.DROPDOWN_NAME = this.getModelAsString();
    this.taskQueue.queueMicroTask(() => this.selectedIdInternalChange = false);
  }

  public searchTextChanged(newVal, oldVal) {
    if (newVal != oldVal) {
      this.filterValues.forEach(filterValue => {
        filterValue.hide = !(filterValue.name.toUpperCase().includes(newVal.toUpperCase()));
      });
    }
  }

  public getModel(): any {
    return this.isFilterActive() ? { filterType: 'set', values: this.selectedIds } : null;
  }

  public setModel(model: any): void {
    if (model == null) {
      if (this.isDestroyed()) {
        this.dispose();
        this.filterValues = [];
      } else {
        this.resetFilter();
      }
    } else if (model.filterType == 'set') {
      this.getCategories(model.values);
    }
    this.setSelectedValues();
    this.filterChangedCallback();
  }

  public getValue(): Array<EnumerationTypeCategorySelected> {
    return this.filterValues.filter(x => x.selected);
  }

  public getModelAsString?(): string {
    return '';
  }

  public setupContentGuiSideFilter(): string {
    return `
    <div class="col s12 m12 l12">
      <ui-field label.bind="params.colDef.headerName">
        <ui-select t="[placeholder]general.chooseAnOption" value.bind="selectedIds" multiple>
          <option repeat.for="value of filterValues" value.bind="value.category">
            \${value.name | t }
          </option>
        </ui-select>
      </ui-field>
    </div>`;
  }
}
