
import { Box } from './../../../modules/box';
import { GlobalLoaderService } from './../../../resources/global-loader/global-loader-service';
import { I18N } from 'aurelia-i18n';
import { BindingEngine, Disposable, autoinject, bindable, computedFrom, customElement, observable } from 'aurelia-framework';
import { BindingSignaler } from 'aurelia-templating-resources';
import { ImportResults, ImportRow, ImportRowAction, ImportService, ImportSession } from "../../service/import-service";
import { IGetRowsParams } from 'ag-grid-community';
import { ImportCorrectErrors } from '../../import-correct-errors/import-correct-errors';
import { DisplayContext } from '../../import-data-index';
import { UIInternal } from '../../../utils/ui-internal';

export class CustomDatasource {
  /**
   *
   */
  constructor(private session: ImportSession, private importService: ImportService) {

  }
  async getRowsForSelect2(params: IGetRowsParams, filterText: string, filterProperties: string[]) {
    const propertyName = params.sortModel[0].colId;
    let list = (await this.importService.getPossibleValues(this.session.contextName, this.session.id, propertyName)).sort((a, b) => {
      return a.localeCompare(b);
    });
    if (filterText?.length > 0) {
      list = list.filter(l => l.toLocaleLowerCase().indexOf(filterText) >= 0);
    }
    params.successCallback(list.map(v => {
      let val = {};
      val[propertyName] = v;
      return val;
    }), list.length);
  }
}
@autoinject
@customElement("import-step-4")
export class ImportStep4 {

  @bindable
  public session: ImportSession;

  @bindable
  public customParameters: number;
  @bindable
  public errorTableView: any;
  @bindable
  public newStep;
  @bindable
  public cancelSession;

  importedRows: ImportRow[] = [];
  uiTableVM: any;

  listService;
  @observable
  hideCorrectedError: boolean = true;
  @bindable
  public importResults: ImportResults;

  private dataIsLoaded: boolean = false;

  nbOfError: number = 0;
  technicalFields = ["errors", "isValid", "id", "importSessionId", "lineNr", "edit", "actions", "serverErrors"]
  disposables: Disposable[] = [];
  uiTable: any;
  floatingImportCorrectErrorsOpen: any;
  displayContext: DisplayContext = 'step4';
  rowUpdating: number | null;
  @observable
  selectedErrors: ImportRow[];

  constructor(private importService: ImportService, private globalLoader: GlobalLoaderService, private i18n: I18N, private bindingEngine: BindingEngine, private box: Box, private signaler: BindingSignaler) {
    this.selectedErrors = [];

  }
  filter(data: ImportRow) {
    if (this.hideCorrectedError == true) {
      return !data.isValid && (data.ignoreRow == null || data.ignoreRow == false);
    }
    return true;
  }
  hideCorrectedErrorChanged() {
    this.uiTableVM?.filterChanged();
  }
  toggleEdit(data) {
    data.edit = true;
  }
  @computedFrom('selectedErrors.length')
  get commonActions() {
    if (this.selectedErrors?.length == 0) return null;
    // intersections between all actions of selected rows
    let intersections = this.selectedErrors[0].actions;
    for (let index = 1; index < this.selectedErrors?.length; index++) {
      const nextElt = this.selectedErrors[index];
      intersections = intersections.filter(x => nextElt.actions?.findIndex(n => n.actionId == x.actionId) >= 0);

    }
    return intersections;
  }
  public async bind() {
    setTimeout(() => {
      this.loadData()
    }, 800);
    setTimeout(() => {
      $(this.uiTable).on('select2:select', (ev) => {
        let data = ev.params.data;
        const uiSelectElt = ev.target.parentElement as HTMLElement;
        const uiSelect = (uiSelectElt.au as any)['ui-select'].viewModel;
        const record = uiSelect?.parentContext.bindingContext as ImportRow;
        let fieldName = uiSelect.optionsText;
        if (!this.floatingImportCorrectErrorsOpen) {
          const existingError = record.serverErrors.find(x => x.propertyName.toLowerCase() == fieldName.toLowerCase());
          if (existingError != null) {
            const shouldDisplayCorrectErrorsBox = this.importedRows.filter(x => !x.isValid && x[fieldName] == existingError.errorValue).length > 1;
            if (shouldDisplayCorrectErrorsBox == true) {
              let box = new ImportCorrectErrors(this.session,
                fieldName,
                record,
                this.globalLoader,
                this.importService,
                this.errorTableView,
                this.importedRows.filter(x => !x.isValid));
              this.floatingImportCorrectErrorsOpen = true;

              this.box.showFloatingBox(box).whenClosed(() => {
                // let's time to aurelia to update property 
                UIInternal.nextTick(() => {
                  this.floatingImportCorrectErrorsOpen = false;
                  this.hideCorrectedErrorChanged();
                  this.nbOfError = this.importedRows.filter(x => x.isValid == false).length;
                  this.importedRows.forEach(r => this.assignErrorProperties(r));
                });
              });
            }
          }
        }
      });
    }, 2000);

  }
  public async loadData() {
    this.dataIsLoaded = false;
    const importedRows = await this.importService.getErrors(this.session.contextName, this.session.id);
    this.listService = new CustomDatasource(this.session, this.importService);
    this.importedRows.splice(0);
    importedRows.forEach(r => {
      r.edit = false;
      this.constructMenuItems(r);
      Object.keys(r).filter(k => this.technicalFields.indexOf(k) < 0).forEach((k, i) => {
        this.disposables.push(this.addPropertyObserver(r, k));
      });
      this.assignErrorProperties(r);
    })
    this.importedRows.push(...importedRows);
    this.dataIsLoaded = true;
    this.nbOfError = this.importedRows.filter(x => x.isValid == false).length;
  }

  public unbind() {
    this.importedRows.splice(0);
    this.disposables.forEach(d => d.dispose());
  }
  public executeCommonAction(action: ImportRowAction) {
    if (this.selectedErrors?.length > 0) {
      let promises: Promise<any>[] = [];
      this.selectedErrors.forEach(async s => promises.push(this.executeAction(s, action)));
      Promise.all(promises);
      this.selectedErrors.splice(0);
    }

  }
  public openExcelFile() {
    window.open("https://view.officeapps.live.com/op/view.aspx?src=" + this.session.importFileSessions.fileUri, "_blank");
  }
  private async executeAction(row: ImportRow, action: ImportRowAction) {
    const updatedRow = await this.importService.executeAction(this.session.contextName, this.session.id, row.id, action.actionId)
    this.updateExistingRow(updatedRow)
  }
  private addPropertyObserver(row: ImportRow, prop: string): Disposable {
    return this.bindingEngine.propertyObserver(row, prop).subscribe(async (n, o) => {
      if (this.floatingImportCorrectErrorsOpen == true) return;
      if (this.rowUpdating === row.id) return;
      const updatedRow = await this.importService.saveImportRowProperty(this.session.contextName, this.session.id, row.id, prop, n);
      this.updateExistingRow(updatedRow);
    });
  }

  private updateExistingRow(updatedRow: ImportRow) {
    const existingRow = this.importedRows.find(x => x.id == updatedRow.id);
    this.rowUpdating = existingRow.id;
    existingRow.isValid = updatedRow.isValid;
    existingRow.actions = updatedRow.actions;
    Object.keys(existingRow).filter(x => this.technicalFields.indexOf(x) < 0).forEach(key => {
      existingRow[key] = updatedRow[key];
    });
    if (existingRow.serverErrors == null) {
      existingRow.serverErrors = [];
    }
    else {
      existingRow.serverErrors.splice(0);
    }
    existingRow.serverErrors.push(...updatedRow.serverErrors);
    this.constructMenuItems(existingRow);
    this.assignErrorProperties(existingRow);
    this.uiTableVM.filterChanged();
    this.nbOfError = this.importedRows.filter(x => x.isValid == false).length;

    UIInternal.nextTick(() => this.rowUpdating = null);
  }


  async confirm() {
    this.importResults = await this.importService.process(this.session.contextName, this.session.id);
    this.newStep();
  }
  private constructMenuItems(row: ImportRow) {
    row.menu = this.getErrorRowContextMenu(row);
  }
  private assignErrorProperties(row: ImportRow) {
    Object.keys(row).filter(v => v.startsWith('errors')).forEach(errorPropKey => {
      row[errorPropKey] = null;
    });
    row.serverErrors?.forEach(se => {
      row['errors' + se.propertyName] = [se.error];
    });

  }
  public getErrorRowContextMenu(currentThis: ImportRow) {
    const menu = [
      {
        group: "1",
        hiddenLabel: true,
        items: []
      }
    ];
    menu[0].items = currentThis.actions?.map(a => {
      return {
        "label": this.i18n.tr(a.labelTranslationKey),
        "icon": a.icon,
        handler: async () => await this.executeAction(currentThis, a)
      };
    }
    );
    return menu;
  }
}
