import { Container, TaskQueue } from 'aurelia-framework';
import { ServiceBase } from './../../../data/service-base';
import { User, ValueSelected, UserPreFilter } from './../../../model/common-model';
import { BindingEngine, Disposable, observable } from "aurelia-binding";
import { Utils } from './../../../utils/utils';

const MAX_SET_MODEL_RETRIES = 50;
export class UserFilterHelper {
    @observable private userSearch: string;
    @observable public selectedUserPreFilter: number;
    private groupName: string;
    public userValues: Array<ValueSelected>;
    private disposables: Array<Disposable> = [];
    private disposableUserValues: Array<Disposable>;
    private bindingEngine: BindingEngine;
    private taskQueue: TaskQueue;
    public selectedIds: Array<number> = [];
    public selectedIdInternalChange: boolean = false;
    private selectedIdsObserver: Disposable;
    public userValuesSelected: Array<ValueSelected> = [];
    private ignoreUserValuesChanged: boolean;
    private ignoreSelectedUserPreFilterChanged: boolean;
    private currentSetModelRetries: number = 0;
    public displayEmptyFields: boolean = false;
    @observable public isGettingUsers: boolean = false;
    @observable public setModelTemp: Function;

    constructor(private service: ServiceBase<User>,
        private firstTime: boolean = true,
        private setInstanceModel: (model) => void,
        private setDropdownName: () => void
    ) {
        this.bindingEngine = Container.instance.get(BindingEngine);
        this.taskQueue = Container.instance.get(TaskQueue);
        this.groupName = Math.random().toString();
        this.getUsers()
    }

    public resetFilter() {
        this.resetUserPreFilter();
        this.resetUserValues();
        this.displayEmptyFields = false;
    }

    public resetUserValues() {
        if (this.userValues != null && Array.isArray(this.userValues)) {
            this.userValues.forEach(userValue => {
                userValue.selected = false;
            });
        }
    }

    public isFilterActive(): boolean {
        if (this.selectedUserPreFilter != null || this.displayEmptyFields) {
            return true;
        }
        if (this.userValues != null && Array.isArray(this.userValues)) {
            return this.userValues.some(x => x.selected);
        }
        return false;
    }

    private isGettingUsersChanged(newVal, oldVal) {
        if (newVal != oldVal && !this.isGettingUsers && this.setModelTemp != null) {
            this.setModelTemp();
        }
    }

    public setModel(model: any): void {
        this.selectedUserPreFilter = model?.userPreFilter; //set directly for applied view
        if (this.isGettingUsers) {
            this.setModelTemp = () => this.setModel(model);
            return;
        }
        this.currentSetModelRetries++;
        if (this.currentSetModelRetries > MAX_SET_MODEL_RETRIES) {
            console.error(`Grid column UserFilter - Max retries reached, cannot set model. Filters and views may be out of order.`);
            return;
        }

        if (this.userValues == null) {
            console.warn(`Grid column UserFilter - OriginalValues not loaded, cannot reset filter. Current retry counter is ${this.currentSetModelRetries}/${MAX_SET_MODEL_RETRIES}. Try again in 200ms.`);
            setTimeout(() => this.setModel(model), 200);
        }
        else {
            this.currentSetModelRetries = 0;
            this.internalSetModel(model);
        }
    }


    private internalSetModel(model: any) {
        if (model == null) {
            this.selectedUserPreFilter = null;
            this.userValues = new Array<ValueSelected>();
            this.userValuesSelected.splice(0);
            this.selectedIds.splice(0);
        }
        else if (model.filterType == 'user') {
            if (this.selectedUserPreFilter == null) {
                // this.userValues = model.filter as Array<ValueSelected>;

                this.userValuesSelected.splice(0);
                this.selectedIds.splice(0);
                this.userValues.forEach(x => x.selected == false);

                if (model.filter != null) {
                    const filterCopy = JSON.parse(JSON.stringify(model.filter));
                    filterCopy.forEach(x => {
                        let user = this.userValues.find(u => u.id == x.id);
                        if (user != null) {
                            user.selected = x.selected;
                        }
                    })
                }
                this.userValuesSelected.push(...this.userValues.filter(x => x.selected));
                this.selectedIds.push(...this.userValuesSelected.map(x => x.id));
            }
        }
        this.applyObservers();
    }

    public async onOpenDropDown(oldUserValues) {
        oldUserValues.forEach(oldUserValue => {
            if (this.userValues && this.userValues.some(x => x.name == oldUserValue.name)) {
                this.userValues.find(x => x.name == oldUserValue.name).selected = oldUserValue.selected;
            }
        });
        this.userValuesSelected.splice(0);
        this.userValuesSelected.push(...this.userValues.filter(x => x.selected));
        this.selectedIds.splice(0);
        this.selectedIds.push(...this.userValuesSelected.map(x => x.id));
    }

    public setupContentGui() {
        return `
      <div class="floating-filter__body__user">
        <div class="floating-filter__body__search">
            <ui-field t="[label]common:grid.searchShort">
                <ui-input type="text" value.bind="filterHelper.userSearch & debounce:500" clear>
                    <ui-input-addon icon="digi-search"></ui-input-addon>
                </ui-input>
            </ui-field>
            <div class="ui-check__wrapper" if.bind="showEmptyFieldsCheckbox">
                <ui-checkbox checked.bind="filterHelper.displayEmptyFields">\${ 'grid.displayEmptyFields' | t }</ui-checkbox>
            </div>
        </div>

        <div class="floating-filter__body__user__radio">
          <div>
            <ui-radio name="${this.groupName}" model.one-time="1" checked.bind="filterHelper.selectedUserPreFilter"><span t="[html]common:grid.me"></span></ui-radio>
          </div>
          <div>
            <ui-radio name="${this.groupName}" model.one-time="2" checked.bind="filterHelper.selectedUserPreFilter"><span t="[html]common:grid.notMe"></span></ui-radio>
          </div>
        </div>       

        <div class="floating-filter__body__user__list">
          <div repeat.for="userValue of filterHelper.userValues" show.bind="!userValue.hide" class="floating-filter__body__user__list__elem">
            <ui-checkbox checked.bind="userValue.selected" title.bind="userValue.name">
              \${userValue.name}
            </ui-checkbox>
          </div>
        </div>
        
      </div>`;
    }

    public getValue(): Array<ValueSelected> | number {
        if (this.userValuesSelected.length > 0) {
            return this.userValuesSelected;
        }
        else {
            return this.selectedUserPreFilter;
        }
    }

    public setupContentGuiSideFilter(): string {
        return `
        <div class="col s12 m12 l12">
            <ui-field label.bind="params.colDef.headerName">
                <ui-select multiple t="[placeholder]general.chooseAnOption" value.bind="filterHelper.selectedIds"
                    data-source.bind="filterHelper.service.gridDataSource" options-text="fullName"
                    filter-properties.bind="['fullName']" options-value="id">
                    <option></option>
                    <option repeat.for="value of filterHelper.userValuesSelected" value.bind="value.id" selected.bind="true">
                    \${value.name}</option>
                    <ui-select-header-template>
                        <div class="ui-check__wrapper" if.bind="showEmptyFieldsCheckbox">
                            <ui-checkbox checked.bind="filterHelper.displayEmptyFields">\${ 'grid.displayEmptyFields' | t }</ui-checkbox>
                        </div>
                    </ui-select-header-template>
                </ui-select>
            </ui-field>
        </div>`;
    }

    public async getUsers(applyObservers: boolean = true) {
        this.isGettingUsers = true;
        let users = await this.service.getEntities(this.service.gridDataSource.predicates, this.service.gridDataSource.expands, this.service.gridDataSource.queryParameters);;
        this.userValues = users
            .filter((u, index) => !Utils.isNullOrEmpty(u.fullName) && users.indexOf(u) === index)
            .map(u => {
                return { id: u.id, name: u.fullName, selected: false, hide: false }
            });
        if (applyObservers) {
            this.applyObservers();
        }
        this.userValues.sortBy("name");
        this.isGettingUsers = false;
    }

    protected applyObservers() {
        this.dispose(); // Making sure that everything is disposed before re-adding observers
        this.disposableUserValues = new Array<Disposable>();
        this.userValues?.forEach(obj =>
            this.disposableUserValues.push(this.bindingEngine.propertyObserver(obj, 'selected').subscribe(this.userValuesChanged.bind(this)))
        );
        this.selectedIdsObserver = this.bindingEngine.collectionObserver(this.selectedIds).subscribe(async (changeRecords) => {
            if (!this.selectedIdInternalChange) {
                if (this.userValues == null || this.userValues.length == 0) {
                    await this.getUsers(false);
                    this.userValues?.forEach(obj =>
                        this.disposableUserValues.push(this.bindingEngine.propertyObserver(obj, 'selected').subscribe(this.userValuesChanged.bind(this)))
                    );
                }
                if (changeRecords?.length > 0 && changeRecords[0].removed.length > 0) {
                    changeRecords[0].removed.forEach(id => {
                        let value = this.userValues?.find(x => x.id == id);
                        if (value != null) {
                            value.selected = false;
                        }
                    });
                }
                this.selectedIds.forEach(id => {
                    let value = this.userValues?.find(x => x.id == id);
                    if (value != null) {
                        value.selected = true;
                    }
                })
            }
        });
        this.disposables.push(this.bindingEngine.propertyObserver(this, 'displayEmptyFields').subscribe(this.userValuesChanged.bind(this)));
    }

    public removeDisposables(all: boolean) {
        if (all && this.disposableUserValues) {
            this.disposableUserValues.forEach(x => x.dispose());
        }
        this.dispose();
    }

    public dispose() {
        this.disposableUserValues?.forEach(x => x.dispose());
        this.selectedIdsObserver?.dispose();
    }

    public setFirstTime(firstTime) {
        this.firstTime = firstTime;
    }

    private userSearchChanged(newVal: string, oldVal: string) {
        if (newVal != oldVal && !this.firstTime) {
            this.userValues.forEach(userValue => {
                userValue.hide = !(userValue.name.toUpperCase().includes(newVal.toUpperCase()));
            });
        }
    }

    private userValuesChanged(newVal: boolean, oldVal: boolean) {
        if (this.ignoreUserValuesChanged) return;
        if (newVal != oldVal) {
            this.selectedIdInternalChange = true;
            this.ignoreSelectedUserPreFilterChanged = true;
            this.userValuesSelected.splice(0);
            this.userValuesSelected.push(...this.userValues.filter(x => x.selected));
            this.selectedIds.splice(0);
            this.selectedIds.push(...this.userValuesSelected.map(x => x.id));
            this.setDropdownName();
            this.setInstanceModel({ filterType: 'user', filter: this.userValues, displayEmptyFields: this.displayEmptyFields });
            this.resetUserPreFilter();
            this.taskQueue.queueMicroTask(() => this.ignoreSelectedUserPreFilterChanged = false);
            this.selectedIdInternalChange = false;
        }
    }

    private selectedUserPreFilterChanged(newVal: number, oldVal: number) {
        if (this.ignoreSelectedUserPreFilterChanged) return;
        if (newVal != oldVal && !this.firstTime) {
            this.ignoreUserValuesChanged = true;
            let model = { filterType: 'user', displayEmptyFields: this.displayEmptyFields };
            if (this.selectedUserPreFilter == UserPreFilter.Me) {
                this.setInstanceModel(Object.assign(model, { type: 'equals', userPreFilter: this.selectedUserPreFilter }));
                this.resetUserValues();
            }
            else if (this.selectedUserPreFilter == UserPreFilter.NotMe) {
                this.setInstanceModel(Object.assign(model, { type: 'notEqual', userPreFilter: this.selectedUserPreFilter }));
                this.resetUserValues();
            }
            else {
                this.setInstanceModel(Object.assign(model, { filter: null }));
            }
            this.taskQueue.queueMicroTask(() => this.ignoreUserValuesChanged = false);
        }
    }

    private resetUserPreFilter() {
        this.selectedUserPreFilter = null;
    }
}