import { BindingEngine, Disposable } from 'aurelia-framework';
import { bindingMode, OverrideContext, Parser } from "aurelia-binding";
import { bindable, customElement } from "aurelia-templating";
import * as breeze from 'breeze-client';
import { EditingModeEnum } from "./../../vm/editing-mode-enum";
import { EnumerationTypeService } from './../../services/enumeration-type-service';
import { Box } from './../../modules/box';
import { Various } from './../../model/common-model';
import { EnumerationTypeDetail } from './../../vm/enumeration-types/enumeration-type-detail';
import { I18N } from 'aurelia-i18n';
import { Config } from './../../config';
import { FilterQueryOp, Predicate } from 'breeze-client';

@customElement('ui-enumeration-select')
export class UIEnumerationSelect {
  @bindable({ defaultBindingMode: bindingMode.twoWay }) public value: string;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public enumerationCategory: number;
  @bindable({ defaultBindingMode: bindingMode.toView }) public isRequired: boolean;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public sortColumn: string;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public propertyId: string;
  @bindable({ defaultBindingMode: bindingMode.toView }) public isCreation: boolean = null;
  @bindable({ defaultBindingMode: bindingMode.toView }) public disabled: boolean = false;
  @bindable({ defaultBindingMode: bindingMode.toView }) public showTag: boolean = true;
  @bindable({ defaultBindingMode: bindingMode.toView }) public readonly: boolean = false;
  @bindable({ defaultBindingMode: bindingMode.toView }) public enumerationTypeService: EnumerationTypeService;
  @bindable({ defaultBindingMode: bindingMode.toView }) public placeholder: string;
  @bindable({ defaultBindingMode: bindingMode.toView }) public hasForeignKeyLink: boolean = true;

  //Creation
  @bindable({ defaultBindingMode: bindingMode.toView }) public canCreate: boolean = false;
  @bindable({ defaultBindingMode: bindingMode.toView }) public createFn: Function;
  @bindable({ defaultBindingMode: bindingMode.toView }) public createButtonLabelTr: string;
  @bindable({ defaultBindingMode: bindingMode.toView }) public createDialogLabelTr: string;

  private propertyName: string;
  private entity: any;
  private entityObserverDispsable: Disposable;

  constructor(private element: Element, private parser: Parser, private bindingEngine: BindingEngine, private box: Box, private i18n: I18N, private config: Config) {
    this.disabled = element.hasAttribute("disabled");
  }

  public bind(bindingContext, overrideContext: OverrideContext) {
    if (this.enumerationTypeService == null) {
      this.enumerationTypeService = new EnumerationTypeService(this.enumerationCategory);
    }

    let enumerationTypeCategory = this.config.enumerationTypeConfig.enumerationTypeCategories?.find(x => x.category == this.enumerationCategory);
    this.createDialogLabelTr ??= enumerationTypeCategory?.name ?? "common:enumerationtype.addEnumerationtype";
    this.createButtonLabelTr ??= enumerationTypeCategory?.addName ?? "common:enumerationtype.addEnumerationtype";

    if (this.isCreation == null && bindingContext != null) {
      // try to get editingModel from bindingContext (work in case of bindingcontext is a view-model heriting from detail-view-model-base)
      // only if we didn't set a value manually
      this.isCreation = bindingContext.editingMode == EditingModeEnum.Create;
    }

    if (this.value?.length > 0) {
      const values = this.value.split('.');
      if (values.length > 1) {
        this.propertyName = values.pop();
        let entityName = values.join('.');

        const entityObserver = this.bindingEngine.expressionObserver(bindingContext, entityName);
        this.entityObserverDispsable = entityObserver.subscribe((n, o) => this.entityChanged(n, o));

        const valueExpression = this.parser.parse(entityName);
        const scope = {
          bindingContext: bindingContext,
          overrideContext: overrideContext
        };
        const entity = valueExpression.evaluate(scope);
        if (entity != null) {
          this.entityChanged(entity, null);
        }
      } else if (values.length == 1) {
        this.propertyName = this.value;
        this.entityChanged(bindingContext, null);
      }
    }
  }

  public entityChanged(n: any, o: any): void {
    this.entity = n;
    if (!this.hasForeignKeyLink) {
      this.propertyId = this.propertyName + "Id";
    } else if (this.entity != null && (this.propertyId == null || this.isRequired == null)) {
      var navProp = (this.entity as breeze.Entity).entityType.navigationProperties.find(fkp => fkp.name == this.propertyName);
      if (navProp?.relatedDataProperties?.length > 0) {
        const dataProp = navProp.relatedDataProperties[0];
        if (this.propertyId == null) {
          this.propertyId = dataProp.name;
        }
        if (this.isRequired == null) {
          this.isRequired = !dataProp.isNullable;
        }
      }
    }
    this.initValue(!this.hasForeignKeyLink);
  }

  public detached() {
    this.entityObserverDispsable?.dispose();
  }

  public isRequiredChanged() {
    this.initValue();
  }

  public isCreationChanged() {
    this.initValue();
  }

  private async initValue(setFk: boolean = false) {
    if (this.entity != null && this.propertyName != null && this.propertyId != null) {
      if (this.entity[this.propertyId] == null && this.entity[this.propertyName] == null && this.isCreation) {
        this.entity[this.propertyName] = await this.enumerationTypeService.getDefault();
        if (this.isRequired && this.entity[this.propertyName] == null && await this.enumerationTypeService.getCount() == 1) {
          this.entity[this.propertyName] = await this.enumerationTypeService.getFirst();
        }
      }

      if (setFk && this.entity[this.propertyName] != null) {
        this.entity[this.propertyId] = this.entity[this.propertyName].id;
      }
    }
  }

  public creationFunction() {
    if (this.createFn) {
      this.createFn();
    } else {
      this.box.showEditDialog(EnumerationTypeDetail, Various.NewId, this.i18n.tr(this.createDialogLabelTr), { model: { param2: this.enumerationCategory } })
        .whenClosed((result) => {
          if (!result.wasCancelled && result.output) {
            this.entity[this.propertyName] = result.output;
          }
        });
    }
  }

  private async onChange(value?: any) {
    if (value != null && this.entity[this.propertyName]?.id != value) {
      this.entity[this.propertyName] = await this.enumerationTypeService.firstEntity(new Predicate("id", FilterQueryOp.Equals, value));
    }
  }
}
