import { IDatasource, IGetRowsParams } from "ag-grid-community";
import { QueryResults } from './repository';
import * as breeze from 'breeze-client';
import { PredicateUtils } from "./predicate-utils";
import { Container } from 'aurelia-framework';
import { OrderByOption, PagingOption } from "./service-base";

export interface IGetPredicatesFunc {
  (filterText: string): breeze.Predicate;
}
export interface IGetDataFunc<T extends breeze.Entity> {
  (params: IGetRowsParams, expands?: any, quickFilter?: string, queryCriteria?: any, queryParameters?: any): Promise<QueryResults<T>>;
}
export class GridDataSource<T extends breeze.Entity> implements IDatasource {
  private predicateUtils: PredicateUtils;

  constructor(private entityTypeName: string, private metadataStore: breeze.MetadataStore) {
    this.filterText = "";
    this.predicates = null;
    this.predicateUtils = Container.instance.get(PredicateUtils);
  }

  public predicates: breeze.Predicate;
  public selectPropertyPaths: string[] | null;
  public getPredicatesFunction: IGetPredicatesFunc;
  public getDataFunction: IGetDataFunc<T>;
  public onSuccessGetRowsCallback: (rowsThisBlock: T[], lastRow?: number) => Promise<T[]>;
  public quickFilter: string;
  public filterText: string;
  public rowCount: number;
  public expands: string[];
  public queryCriteria: any;
  public queryParameters: any;
  public queryOrderBy: OrderByOption;
  public queryPaging: PagingOption;

  public customSelect2Predicates: (filterText: string, filterProperties: string[]) => breeze.Predicate;
  public customSelect2QueryParameters: () => object;

  private get entityType(): breeze.EntityType | breeze.ComplexType {
    return this.metadataStore.getEntityType(this.entityTypeName)
  }

  getRows(params: IGetRowsParams) {
    if (this.getPredicatesFunction != null) {
      this.predicates = this.getPredicatesFunction(this.filterText);
    }
    var promise = this.getDataFunction(params, this.expands, this.quickFilter, this.queryCriteria, this.queryParameters);
    promise
      .then(async (results) => {
        if (this.onSuccessGetRowsCallback != null) results.results = await this.onSuccessGetRowsCallback(results.results, results.fullCount);
        params.successCallback(results.results, results.fullCount);
      })
      .catch((reason) => params.failCallback());
    return promise;
  }

  private getProp(property: string): breeze.DataProperty {
    return this.getPropHelper(property.split('.'), this.entityType);
  }
  private getPropHelper(property: string[], entityType: breeze.EntityType | breeze.ComplexType): breeze.DataProperty {
    if (property.length > 1) {
      let propName = property.shift();
      if (entityType.hasOwnProperty('navigationProperties')) {
        let navProp = (<breeze.EntityType>entityType).navigationProperties.find(navProp => navProp.name == propName);
        if (navProp) {
          return this.getPropHelper(property, navProp.entityType);
        }
      }
      if (entityType.hasOwnProperty('complexProperties')) {
        let complexProp = (<breeze.ComplexType>entityType).complexProperties.find(complexProp => complexProp.name == propName);
        if (complexProp && complexProp.dataType.hasOwnProperty('dataProperties')) {
          return this.getPropHelper(property, <any>complexProp.dataType);
        }
      }
      return null;
    }
    return entityType.dataProperties.find(dataProp => dataProp.name == property[0]);
  }

  getRowsForSelect2(params: IGetRowsParams, filterText: string, filterProperties: string[]) {
    this.predicates = this.getPredicatesForSelect2(filterText, filterProperties);
    var promise = this.getDataFunction(params, this.expands, this.quickFilter, this.queryCriteria, this.getQueryParametersForSelect2());
    promise
      .then((results) => {
        params.successCallback(results.results, results.fullCount);
      })
      .catch((reason) => params.failCallback());
    return promise;
  }

  getPredicatesForSelect2(filterText: string, filterProperties: string[]) {
    var predicates: breeze.Predicate = null;
    if (filterText != null && filterText.length > 0 && filterProperties != null && filterProperties.length > 0) {
      filterProperties.forEach(fp => {
        let prop = this.getProp(fp);
        let isNumericProp = true === prop?.dataType.isNumeric;
        let queryOp = isNumericProp ? breeze.FilterQueryOp.Equals : breeze.FilterQueryOp.Contains;

        let tempPredicates: Array<breeze.Predicate> = this.predicateUtils.getPredicates(filterText, fp, prop?.dataType, queryOp);
        if (tempPredicates.length > 0) {
          predicates = predicates == null ? breeze.Predicate.and(...tempPredicates) : predicates.or(breeze.Predicate.and(...tempPredicates));
        }
      });
    }
    let custom: breeze.Predicate = null;
    if (this.customSelect2Predicates != null) {
      custom = this.customSelect2Predicates(filterText, filterProperties);
    }
    return predicates && custom ? predicates.and(custom) : (predicates || custom);
  }

  getQueryParametersForSelect2(): object {
    return Object.assign({}, this.queryParameters,
      this.customSelect2QueryParameters ? this.customSelect2QueryParameters() : {});
  }
} 