import { ServiceBase } from './../../data/service-base';
import { bindable, bindingMode, customElement, observable } from "aurelia-framework";
import * as breeze from 'breeze-client';
import { IGetRowsParams } from "ag-grid-community";
import { Predicate, FilterQueryOp } from 'breeze-client';

@customElement("ui-tag-select")
export class UITagSelect<TLinkEntity extends breeze.Entity, TTargetEntity extends breeze.Entity> {
  @bindable()
  public placeholder: string = "";
  @bindable()
  public disabled: string | boolean = false;

  @observable private model: TTargetEntity;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public entity: breeze.Entity;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public optionsText: string;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public targetArrayName: string;
  @bindable({ defaultBindingMode: bindingMode.twoWay }) public options: Array<TTargetEntity>;
  public tags: Array<TLinkEntity>;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public targetService: ServiceBase<TTargetEntity> = null;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public linkedService: ServiceBase<TLinkEntity> = null;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public callback: ({ targetEntity }) => {};

  private isSelectVisible: boolean = false;
  private linkedIdName: string;
  private targetIdName: string;
  private targetName: string;
  private uISelect: HTMLElement;
  private uISelectInput: HTMLInputElement | HTMLTextAreaElement;

  protected attached() {
    this.uISelectInput = (this.uISelect.getElementsByTagName("input").item(0) as HTMLInputElement);

    let navigationProperty = this.entity.entityType.navigationProperties.find(fkp => fkp.name == this.targetArrayName);
    this.linkedIdName = navigationProperty.invForeignKeyNames[0];

    let fkPropTarget = navigationProperty.entityType.foreignKeyProperties.find(p => p.name != this.linkedIdName && p.name != 'createdById' && p.name != 'updatedById');
    this.targetName = fkPropTarget.relatedNavigationProperty.name;
    this.targetIdName = fkPropTarget.name;

    this.tags = this.entity[this.targetArrayName];
  }

  protected bind() {
    if (this.targetService == null) throw new Error("Parameter 'targetService' is mandatory");
    if (this.linkedService == null) throw new Error("Parameter 'linkedService' is mandatory");
  }

  public focus() {
    this.isSelectVisible = true;
    setTimeout(() => {
      this.uISelectInput.focus();
    }, 50);
  }

  public blur() {
    this.isSelectVisible = false;
  }

  public async fetchOptions(query: string) {
    if (this.targetService?.gridDataSource != null) {
      let params: IGetRowsParams = {
        startRow: null,
        endRow: null,
        successCallback: null,
        failCallback: () => {
          //empty 
        },
        sortModel: [
          { colId: this.optionsText, sort: 'asc' }
        ],
        filterModel: null,
        context: null
      };
      if (query != null && query != "") {
        params.filterModel = {
          [this.optionsText]: { filterType: "text", type: "contains", filter: query }
        };
      }

      this.targetService.gridDataSource.getPredicatesFunction = () => {
        let predicate: Predicate;
        this.tags.forEach(tag => {
          let tempPredicate: Predicate = new Predicate("id", FilterQueryOp.NotEquals, tag[this.targetIdName]);
          this.targetService.gridDataSource.getPredicatesFunction = () => { let predicate: Predicate = new Predicate("id", FilterQueryOp.NotEquals, tag["id"]); return predicate; };
          if (predicate == null) predicate = tempPredicate;
          else predicate = predicate.and(tempPredicate);
        });
        return predicate;
      };

      this.options = (await this.targetService.gridDataSource.getRows(params)).results;
    }
    return this.options;
  }

  protected async modelChanged(newValue, oldValue) {
    let option: TTargetEntity = this.model;
    if (option != null) {
      await this.addLinked(option);
    }
    this.model = null;
  }

  protected async addTarget(query: string) {
    let targetEntity: TTargetEntity = await this.targetService.createEntity();
    targetEntity[this.optionsText] = query;
    if (this.callback != null) {
      this.callback({ targetEntity });
    }
    await this.addLinked(targetEntity);
  }

  protected async addLinked(targetEntity: TTargetEntity) {
    let linkedEntity: TLinkEntity = await this.linkedService.createEntity();
    linkedEntity[this.linkedIdName] = this.entity["id"];
    linkedEntity[this.targetIdName] = targetEntity["id"];
    this.tags.push(linkedEntity);
    this.blur();
  }

  protected delete(tag: TLinkEntity) {
    if (tag.entityAspect.entityState.isAdded()) {
      if (tag[this.targetName].entityAspect.entityState.isAdded()) {
        tag[this.targetName].entityAspect.setDetached();
      }
      tag.entityAspect.setDetached();
    }
    else {
      tag.entityAspect.setDeleted();
    }
  }
}
