import { ServiceBase } from './../../data/service-base';
import { FilterMode, ValueSelected } from './../../model/common-model';
import * as breeze from 'breeze-client';
import BaseFloatingFilter from './base-floating-filter';
import { BindingEngine, observable } from 'aurelia-binding';
import { FilterChangedEvent, IFloatingFilterParams } from 'ag-grid-community';
import { Container } from 'aurelia-framework';
import { I18N } from 'aurelia-i18n';

export abstract class BaseRelationFloatingFilter<T extends breeze.Entity> extends BaseFloatingFilter {
    protected abstract get filterType(): string;
    public abstract setupContentGuiSideFilter(): string;
    public abstract toggleFilterMode(filterMode: FilterMode);
    public abstract setInstanceModel();


    protected filterValues: Array<ValueSelected>;
    protected selectedIds: Array<number> = [];
    protected service: ServiceBase<T>;
    protected fieldPath: Array<string>;
    protected bindingEngine: BindingEngine;
    protected selectedIdInternalChange: boolean = false;
    protected displayEmptyFields: boolean = false;

    @observable
    private valueSearch: string;

    public async init(params: IFloatingFilterParams & { service: ServiceBase<T> }): Promise<void> {
        this.i18n = Container.instance.get(I18N);
        if (this.i18n != null) {
            this.DROPDOWN_NAME = this.i18n.tr('grid.noFilter', { ns: "common" });
        }
        this.fieldPath = params.column.getColDef().field.split('.');
        this.service = params.service;
        super.init(params);
        await super.setupGui();
    }

    public valueSearchChanged(newVal: string, oldVal: string) {
        if (newVal != oldVal) {
            this.filterValues.forEach(filterValue => {
                filterValue.hide = !(filterValue.name.toLowerCase().includes(newVal.toLowerCase()));
            });
        }
    }

    public async getValues() {
        this.filterValues = new Array<ValueSelected>();
        this.dispose();

        let values = await this.service.getEntities(this.service.gridDataSource.predicates, this.service.gridDataSource.expands, { ...this.service.gridDataSource.queryParameters, $inlinecount: 'allpages' });
        values.forEach((value) => {
            let obj = { id: value["id"], name: this.getFieldValue(value), selected: false, hide: false }
            this.filterValues.push(obj);
            let disposable = this.bindingEngine.propertyObserver(obj, '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?.[0]?.removed.length > 0) {
                        changeRecords[0].removed.forEach(id => {
                            this.filterValues.find(x => x.id == id).selected = false;
                        });
                    }
                    this.selectedIds.forEach(id => {
                        this.filterValues.find(x => x.id == id).selected = true;
                    })
                }
            }),
            this.bindingEngine.propertyObserver(this, 'displayEmptyFields').subscribe(this.filterValuesChanged.bind(this))
        );
        this.filterValues.sortBy("name");
    }

    public getFieldValue(value: T) {
        let fieldValue: any = value;
        this.fieldPath.forEach(fieldName => {
            const newFieldValue = fieldValue[fieldName];
            if (newFieldValue != null) {
                fieldValue = newFieldValue;
            }
        });
        return fieldValue as string;
    }

    public async onOpenDropDown() {
        this.dispose();
        let oldFilterValues = this.filterValues;
        await this.getValues();
        if (oldFilterValues && oldFilterValues.length > 0) {
            oldFilterValues.forEach(oldFilterValue => {
                if (this.filterValues && this.filterValues.some(x => x.name == oldFilterValue.name)) {
                    this.filterValues.find(x => x.name == oldFilterValue.name).selected = oldFilterValue.selected;
                }
            });
            this.selectedIds.splice(0);
            this.selectedIds.push(...this.filterValues.filter(x => x.selected).map(x => x.id));
        }
        else {
            this.resetFilter();
        }
    }

    public filterValuesChanged(newVal: boolean, oldVal: boolean) {
        if (newVal != oldVal && !this.firstTime) {
            this.DROPDOWN_NAME = this.getModelAsString();
            this.selectedIdInternalChange = true;
            this.selectedIds.splice(0);
            this.selectedIds.push(...this.filterValues.filter(x => x.selected).map(x => x.id));
            this.setInstanceModel();
            this.selectedIdInternalChange = false;
        }
    }

    public resetFilter() {
        if (this.filterValues != null) {
            this.filterValues.forEach(filterValue => {
                filterValue.selected = false;
            });
        }
        this.displayEmptyFields = false;
    }

    public onParentModelChanged(parentModel: any, filterChangedEvent?: FilterChangedEvent) {
        if (parentModel) {
            if (parentModel.filter) {
                this.filterValues = (parentModel.filter as Array<ValueSelected>);
            }
        }
        else {
            if (this.filterValues) {
                this.resetFilter();
            }
        }
        this.DROPDOWN_NAME = this.getModelAsString();
    }

    public getModelAsString(): string {
        if (this.filterValues && this.filterValues.some(x => x.selected)) {
            return `${this.i18n.tr('grid.filterOoo', { ns: "common" })} <span style='background-color: gray; color: white; padding: 0 5px 0 5px; border-radius: 20px;'>${this.filterValues.filter(x => x.selected).length}</span>`;
        }
        else {
            return this.i18n.tr('grid.noFilter', { ns: "common" });
        }
    }
}