import { QueryBuilder } from '../ui/Base/QueryBuilder';
import { Assert } from '../misc/Assert';
import { IFacetRequest, IFacetRequestValue } from '../rest/Facet/FacetRequest';
import { QueryEvents } from '../events/QueryEvents';
import { findIndex } from 'underscore';
import { IQueryResults } from '../rest/QueryResults';
import { IDynamicFacet } from '../ui/DynamicFacet/IDynamicFacet';
import { DynamicFacetRequestBuilder } from './DynamicFacetRequestBuilder';

export class DynamicFacetQueryController {
  protected requestBuilder: DynamicFacetRequestBuilder;
  private numberOfValuesToRequest: number;
  private freezeCurrentValues = false;
  private freezeFacetOrder = false;
  private preventAutoSelection = false;

  constructor(protected facet: IDynamicFacet) {
    this.requestBuilder = new DynamicFacetRequestBuilder({
      facetId: this.facet.options.id,
      field: this.facet.fieldName,
      type: this.facet.facetType,
      sortCriteria: this.facet.options.sortCriteria,
      injectionDepth: this.facet.options.injectionDepth,
      filterFacetCount: this.facet.options.filterFacetCount
    });
    this.resetNumberOfValuesToRequest();
    this.resetFlagsDuringQuery();
  }

  private resetFlagsDuringQuery() {
    this.facet.bind.onRootElement(QueryEvents.duringQuery, () => {
      this.freezeCurrentValues = false;
      this.freezeFacetOrder = false;
      this.preventAutoSelection = false;
    });
  }

  public increaseNumberOfValuesToRequest(additionalNumberOfValues: number) {
    this.numberOfValuesToRequest += additionalNumberOfValues;
  }

  public resetNumberOfValuesToRequest() {
    this.numberOfValuesToRequest = this.facet.options.numberOfValues;
  }

  public enablePreventAutoSelectionFlag() {
    this.preventAutoSelection = true;
  }

  public enableFreezeCurrentValuesFlag() {
    if (this.areValuesIncorrectlyAffectedByDependsOn) {
      return;
    }

    this.freezeCurrentValues = true;
  }

  public enableFreezeFacetOrderFlag() {
    this.freezeFacetOrder = true;
  }

  private get areValuesIncorrectlyAffectedByDependsOn() {
    if (!this.facet.dependsOnManager.hasDependentFacets) {
      return false;
    }

    if (this.facet.dependsOnManager.dependentFacetsHaveSelectedValues) {
      return false;
    }

    return this.currentValues.length < this.numberOfValuesToRequest;
  }

  /**
   * Build the facets request for the DynamicFacet, and insert it in the query builder
   * @param queryBuilder
   */
  public putFacetIntoQueryBuilder(queryBuilder: QueryBuilder) {
    Assert.exists(queryBuilder);

    queryBuilder.facetRequests.push(this.buildFacetRequest());
    if (this.freezeFacetOrder) {
      queryBuilder.facetOptions.freezeFacetOrder = true;
    }
  }

  public buildFacetRequest(): IFacetRequest {
    return {
      ...this.requestBuilder.buildBaseRequestForQuery(),
      currentValues: this.currentValues,
      numberOfValues: this.numberOfValues,
      freezeCurrentValues: this.freezeCurrentValues,
      preventAutoSelect: this.preventAutoSelection,
      isFieldExpanded: this.numberOfValuesToRequest > this.facet.options.numberOfValues
    };
  }

  public getQueryResults(): Promise<IQueryResults> {
    const query = this.facet.queryController.getLastQuery();
    // Specifying a numberOfResults of 0 will not log the query as a full fledged query in the API
    // it will also alleviate the load on the index
    query.numberOfResults = 0;

    const previousFacetRequestIndex = findIndex(query.facets, { facetId: this.facet.options.id });
    if (previousFacetRequestIndex !== -1) {
      query.facets[previousFacetRequestIndex] = this.buildFacetRequest();
    } else if (query.facets) {
      query.facets.push(this.buildFacetRequest());
    } else {
      query.facets = [this.buildFacetRequest()];
    }

    return this.facet.queryController.getEndpoint().search(query);
  }

  protected get currentValues(): IFacetRequestValue[] {
    return this.facet.values.allFacetValues.map(({ value, state }) => ({
      value,
      state
    }));
  }

  protected get numberOfValues() {
    if (this.freezeCurrentValues) {
      return this.currentValues.length;
    }

    return Math.max(this.numberOfValuesToRequest, this.facet.values.activeValues.length);
  }
}
