import { Router } from 'aurelia-router';
import { UIDrop } from './../../ui/shared/ui-drop';
import { DialogBoxViewModel } from './../../modules/dialogs/dialog-box';
import { ViewParameterBox } from './../../modules/dialogs/view-parameter-box';
import { DialogCloseResult, DialogOpenResult } from 'aurelia-dialog';
import { EntityTypeNames } from './../../constants/constants';
import { AgGridViewBox } from './../../modules/dialogs/ag-grid-view-box';
import { IAgGridView, BindableGridOptions } from './custom-ag-grid';
import { FilterViewBox } from './../../modules/dialogs/filter-view-box';
import { AgGridViewService } from './../../services/ag-grid-view-service';
import { CsvExportParams, ColDef } from 'ag-grid-community';
import { ProcessCellForExportParams } from 'ag-grid-community';
import { ProcessHeaderForExportParams } from 'ag-grid-community';
import { ModelUpdatedEvent } from 'ag-grid-community';
import { AgGridSortingHelper, GridOptions } from './utilities/ag-grid-helpers';
import { ColumnState, ApplyColumnStateParams } from 'ag-grid-community';
import { autoinject, bindable, observable } from "aurelia-framework";
import { I18N } from 'aurelia-i18n';
import { Disposable, BindingEngine, bindingMode } from 'aurelia-binding';
import { Config } from '../../config';
import { ServiceBase } from './../../data/service-base';
import { AgGridViewDetail } from './ag-grid-view-detail';
import { CustomLogger } from './../../modules/custom-logger';
import { ShowEditDialogOptions, Box } from './../../modules/box';
import { AgGridView, AgGridViewUser, User, FieldType, ActionDialogBoxInputParameters, CsvExportConfig } from './../../model/common-model';
import { Datacontext } from './../../data/data-context';
import { AuthService } from '../../security/auth-service';
import moment from 'moment';
import { autoAdjustColumnsSize, getNewColumnStates } from './../../ag-grid/agUtils';
import { KeyboardShortcut } from './../../constants/constants';

@autoinject
export class GridHeaderBar {
  @bindable title: string;
  @bindable addFn: Function;
  @bindable deleteFn: Function;
  @bindable canAdd: boolean = true;
  @bindable canDelete: boolean = true;
  @bindable canBack: boolean = false;
  @bindable canUseView: boolean = true;
  @bindable canUseFilter: boolean = true;
  @bindable canUseParameters: boolean = true;
  @bindable canResetFilters: boolean;
  @bindable canExport: boolean = true;
  @bindable showViewBox: boolean;
  @bindable canShowAllView: boolean = false;
  @bindable agGridViewList: Array<AgGridView>;
  @bindable({ bindingMode: bindingMode.fromView }) gridOptions: GridOptions;
  @bindable showFilterBox: boolean;
  @bindable showParametersViewBox: boolean;
  @bindable afterResetFilters: Function;
  @bindable datacontext: Datacontext;
  @bindable afterSetAgGridView: (agGridView: IAgGridView) => void;
  @bindable agGridViewModuleView: string;
  @bindable options: BindableGridOptions;
  @bindable selectAll: Function;
  @bindable csvExportConfig: CsvExportConfig;

  private agGridSearch: string;
  private columnSearch: string;

  private dropView: UIDrop;

  @observable private showAllView: boolean = false;
  private async showAllViewChanged(newValue, oldValue) {
    if (newValue != oldValue && this.gridViewService != null) {
      await this.loadAgGridViewData(true);
    }
  }

  protected gridViewService: AgGridViewService;
  private agGridViewUserService: ServiceBase<AgGridViewUser>;

  private agGridSortingHelper: AgGridSortingHelper;
  private fixedFiltersObserverDisabled: boolean = false;
  public disposables: Array<Disposable> = [];

  public filterViewBox: FilterViewBox;
  private agGridViewBox: AgGridViewBox;
  private defaultAgGridView: AgGridView;
  private viewParameterBox: ViewParameterBox;
  private firstTime: boolean;

  constructor(public bindingEngine: BindingEngine, public i18n: I18N, private box: Box, private router: Router, private logger: CustomLogger, private authService: AuthService, private config: Config, private element: Element) {
    this.gridViewService = new AgGridViewService();
    this.agGridViewUserService = new ServiceBase<AgGridViewUser>(EntityTypeNames.AgGridViewUser);

    this.filterViewBox = new FilterViewBox(this);
    this.agGridViewBox = new AgGridViewBox(this, authService, config);
    this.viewParameterBox = new ViewParameterBox(this, config);
    this.firstTime = true;
  }

  public attached() {
    if (this.element?.classList?.add != null) {
      this.element.classList.add('grid-header-bar');
    }

    if (this.gridOptions != null) {
      let propObs = this.bindingEngine.propertyObserver(this.gridOptions, "fixedFilters");
      if (propObs != null) {
        this.disposables.push(
          propObs.subscribe((newValue: boolean, oldValue: boolean) => {
            if (newValue !== oldValue && oldValue != undefined && this.fixedFiltersObserverDisabled) {
              this.toggleFloatingRow();
            }
          }));
      }
    }

    this.agGridSortingHelper = new AgGridSortingHelper(this.gridOptions);

    this.csvExportConfig = Object.assign({ useConfirmDialog: true } as CsvExportConfig, this.csvExportConfig);
  }

  public bind() {
    this.agGridViewModuleView ??= this.router.currentInstruction.config.moduleId;
  }

  private onAdjustColumnsChanged() {
    autoAdjustColumnsSize(this.gridOptions);
  }

  public detached() {
    if (this.disposables?.length) {
      this.disposables.forEach(x => {
        x.dispose();
      })
    }
  }

  public get internalCanResetFilters() {
    if (this.canResetFilters != null && !this.canResetFilters) return false;
    if (this.gridOptions && this.gridOptions.api) {
      return this.gridOptions.api.isAnyFilterPresent() || (this.agGridSortingHelper.hasAnySorting() && false == this.agGridSortingHelper.isCurrentSortDefault());
    }
    return false;
  }

  public resetFilters() {
    let savedDs = (<any>this.gridOptions.api).rowModel.datasource;
    this.gridOptions.api.setDatasource(null);
    this.gridOptions.api.setFilterModel(null);
    this.gridOptions.columnApi.applyColumnState({
      defaultState: {
        sort: null
      }
    });
    this.agGridSortingHelper.applyDefaultSortModel();
    if (this.afterResetFilters != null) {
      this.afterResetFilters();
    }
    this.gridOptions.api.setDatasource(savedDs);
  }

  public async addAgGridView() {
    this.dropView?.closeDrop();
    await this.box.showEditDialog(AgGridViewDetail, -100, this.i18n.tr("grid.addNewView", { ns: "common" }),
      new ShowEditDialogOptions({
        callback: (createdEntity: AgGridView) => {
          createdEntity.moduleId = this.agGridViewModuleView;
          createdEntity.columnState = JSON.stringify(this.gridOptions.columnApi.getColumnState());
          createdEntity.filterModel = JSON.stringify(this.gridOptions.api.getFilterModel());
          createdEntity.fixedFilters = this.gridOptions.fixedFilters ?? false;
          createdEntity.adjustColumns = this.gridOptions.adjustColumns ?? this.config.agGridConfig.defaultAdjustColumns;
        },
        lock: false,
        containerClass: "container-ag-grid-view-detail",
        overlayClass: "overlay-ag-grid-view-detail",
        size: "sm"
      })
    ).whenClosed((result) => {
      if (!result.wasCancelled) {
        let createdAgGridView: AgGridView = result.output;
        this.agGridViewList.push(createdAgGridView);
        this.logger.Log(this.i18n.tr("grid.viewAdded", { ns: "common" }), null, 'CustomAgGrid', true);
        this.setAgGridView(createdAgGridView);
      }
    });
  }

  public async setAgGridView(agGridView: AgGridView) {
    let routeName = this.router.currentInstruction.config.route as string;
    let savedDs = (<any>this.gridOptions.api).rowModel.datasource;

    this.gridOptions.api.setDatasource(null);
    this.gridOptions.columnApi.applyColumnState({
      state: getNewColumnStates(this.gridOptions, JSON.parse(agGridView.columnState) as ColumnState[]),
      defaultState: { sort: null },
      applyOrder: true
    });
    if (agGridView.sortModel != null && agGridView.sortModel != '[]') {
      let temp: ApplyColumnStateParams = {
        state: JSON.parse(agGridView.sortModel) as ColumnState[],
        defaultState: { sort: null },
        applyOrder: true
      }
      this.gridOptions.columnApi.applyColumnState(temp);
    } else {
      if (false == this.agGridSortingHelper.hasAnySorting()) {
        this.agGridSortingHelper.applyDefaultSortModel();
      }
    }

    this.gridOptions.api.setFilterModel(JSON.parse(agGridView.filterModel));
    this.fixedFiltersObserverDisabled = true;
    this.gridOptions.fixedFilters = agGridView.fixedFilters;
    this.gridOptions.adjustColumns = agGridView.adjustColumns;
    this.fixedFiltersObserverDisabled = false;
    this.agGridViewList.filter(x => x.used).forEach(view => view.used = false);
    agGridView.used = true;
    if (!this.showFilterBox && agGridView.fixedFilters != null) {
      this.toggleFloatingRow(agGridView.fixedFilters);
    }
    if (this.authService.currentUser) {
      if (agGridView.isDefaultView) {
        delete this.authService.currentUser.settings.dataGridSettings[routeName];
        autoAdjustColumnsSize(this.gridOptions);
      } else {
        this.authService.currentUser.settings.dataGridSettings[routeName] = {
          usedView: { id: agGridView.id, name: agGridView.name },
          fixedFilters: false,
          adjustColumns: null,
          colsState: null,
          filterModel: null
        }
      }

      if (this.datacontext?.saveUserSettings != null)
        await this.datacontext.saveUserSettings(this.authService.currentUser.sanitizedSettings);
    }
    if (this.afterSetAgGridView != null) {
      this.afterSetAgGridView({ agGridView: agGridView });
    }

    this.gridOptions.api.setDatasource(savedDs);
    this.logger.Log(this.i18n.tr("grid.viewSetted", { ns: "common" }), null, 'CustomAgGrid', true);
  }

  public async toggleFloatingRow(floatingFilter: boolean = null) {
    if (this.gridOptions) {
      if (this.showFilterBox) {
        await this.box.showFloatingBox(this.filterViewBox);
      } else {
        let columnDefs = this.gridOptions.api.getColumnDefs();
        columnDefs.forEach((columnDef: ColDef) => {
          columnDef.floatingFilter = floatingFilter ?? !columnDef.floatingFilter;
        });
        this.gridOptions.api.setColumnDefs(columnDefs);
        this.gridOptions.api.refreshHeader();
      }
    }
  }

  public async showAgGridViewBox() {
    await this.loadAgGridViewData();
    await this.box.showFloatingBox(this.agGridViewBox);
  }

  public async copyAgGridView(agGridView: AgGridView) {
    await this.box.showSelect(this.i18n.tr('grid.createdById', { ns: "common" }), this.i18n.tr('grid.userSelection', { ns: "common" }), new ServiceBase<User>("User"), "fullName", "id", "digi-copy").then((dialogOpenResult: DialogOpenResult) => {
      dialogOpenResult.closeResult.then(async (dialogCloseResult: DialogCloseResult) => {
        if (!dialogCloseResult.wasCancelled) {
          let userIds: Array<number> = dialogCloseResult.output;
          let copiedAgGridViews: Array<AgGridView> = new Array<AgGridView>();
          for (let userId of userIds) {
            let copiedAgGridView: AgGridView = await this.gridViewService.createEntity({
              name: agGridView.name,
              moduleId: agGridView.moduleId,
              fixedFilters: agGridView.fixedFilters,
              adjustColumns: agGridView.adjustColumns,
              columnState: agGridView.columnState,
              filterModel: agGridView.filterModel,
              visibility: "Private",
              authorId: userId
            });
            copiedAgGridViews.push(copiedAgGridView);
          }
          await this.gridViewService.saveEntities(copiedAgGridViews);
          this.agGridViewList.push(...copiedAgGridViews);
          this.logger.Log(this.i18n.tr("grid.viewCopied", { ns: "common" }), null, 'CustomAgGrid', true);
        }
      });
    });
  }

  public async loadAgGridViewData(forceLoad: boolean = false) {
    if ((this.firstTime && this.authService.currentUser) || forceLoad) {
      if (this.agGridViewList != null) {
        let agGridViewData: Array<AgGridView>;
        if (this.config.agGridConfig.allowSharedView) {
          agGridViewData = await this.gridViewService.getAllFromModuleIdWithShared(this.agGridViewModuleView, (this.showAllView ? null : this.authService.currentUser.id));
        }
        else {
          agGridViewData = await this.gridViewService.getAllFromModuleId(this.agGridViewModuleView, (this.showAllView ? null : this.authService.currentUser.id));
        }

        this.firstTime = false;
        if (agGridViewData != null) {
          this.defaultAgGridView = this.agGridViewList.find(x => x.isDefaultView && x.id == -100);

          let defaultAgGridView: AgGridView = this.agGridViewList.find(x => x.isDefaultView);
          let usedAgGridView: AgGridView = this.agGridViewList.find(x => x.used);

          this.agGridViewList.splice(0);
          for (const agGridView of agGridViewData) {
            agGridView.hide = false;
            if (usedAgGridView != null && usedAgGridView.id == agGridView.id) {
              agGridView.used = true;
            }
            this.agGridViewList.push(agGridView);
          }

          if (!this.agGridViewList.some(x => x.isDefaultView)) {
            this.agGridViewList.push(defaultAgGridView);
          }
        }
        this.agGridViewList.sortBy("isDefaultView", false);
      }
    }
  }

  public getCsvExportParams(): CsvExportParams {
    return {
      columnSeparator: ';',
      fileName: this.i18n.tr(this.router.currentInstruction.config.title).replace('.', ''),
      processCellCallback: (params: ProcessCellForExportParams) => {
        if (params.value == null) {
          return '';
        }
        else {
          let colDef = params.column.getColDef();

          switch (colDef.type) {
            case FieldType.Date:
            case FieldType.DateTime:
              return moment(params.value).format(colDef.floatingFilterComponentParams.dateFormat);
            case FieldType.Number:
              return !isNaN(params.value) ? new Intl.NumberFormat(colDef.floatingFilterComponentParams.defaultLocale, { useGrouping: false }).format(params.value) : params.value;
            default:
              break;
          }

          if (colDef.field == "isSelected") {
            if (params.value === true) {
              return this.i18n.tr("general.yes", { ns: "common" });
            }
            else {
              return this.i18n.tr("general.no", { ns: "common" });
            }
          }
        }
        return params.value;
      },
      processHeaderCallback: (params: ProcessHeaderForExportParams) => {
        let colDef = params.column.getColDef();
        if (colDef.field == "isSelected") {
          return this.i18n.tr("grid.isSelected", { ns: "common" });
        }
        return colDef.headerName;
      }
    };
  }

  public async exportToCsv() {
    let buttonOk: ActionDialogBoxInputParameters =
    {
      label: this.i18n.tr("grid.export", { ns: "common" }),
      title: this.i18n.tr("grid.export", { ns: "common" }),
      theme: 'primary',
      type: 'solid',
      disabled: false,
      fn: (thisBox: DialogBoxViewModel) => {
        thisBox.controller.ok();
      },
      keyboardShortcut: this.config.globalConfig.keyboardShortcutFwBox ? KeyboardShortcut.Enter : null
    };
    let buttonCancel: ActionDialogBoxInputParameters =
    {
      label: this.i18n.tr("general.cancel", { ns: "common" }),
      title: this.i18n.tr("general.cancel", { ns: "common" }),
      theme: 'dark',
      type: 'ghost',
      disabled: false,
      fn: (thisBox: DialogBoxViewModel) => {
        thisBox.controller.cancel();
      },
      keyboardShortcut: this.config.globalConfig.keyboardShortcutFwBox ? [KeyboardShortcut.Escape, KeyboardShortcut.Backspace] : null
    };

    if (this.csvExportConfig.useConfirmDialog) {
      await this.box.showQuestion(this.i18n.tr("general.csvExportConfirmation", { ns: "common" }), this.i18n.tr("grid.csvExport", { ns: "common" }), [buttonCancel, buttonOk]).whenClosed();
    }

    if (this.csvExportConfig.export) {
      await this.csvExportConfig.export();
    } else {
      this.gridOptions.api.showLoadingOverlay();
      try {
        let limit = this.gridOptions.api.getModel().getRowCount();
        if (limit > 100000) {
          limit = 100000;
        }

        let nbUpdated = -1;
        this.gridOptions.paginationPageSize = limit;
        this.gridOptions.cacheBlockSize = limit;

        this.gridOptions.onModelUpdated = (event: ModelUpdatedEvent) => {
          nbUpdated++;
          if (nbUpdated == 1) {
            this.gridOptions.api.exportDataAsCsv(this.getCsvExportParams());
          }
        }
        (this.gridOptions.api.getModel() as any).reset();
      }
      finally {
        this.gridOptions.api.hideOverlay();
      }
    }
  }

  public async showParametersBox() {
    await this.box.showFloatingBox(this.viewParameterBox);
  }
}