import { SearchRequest, AggregationsSignificantStringTermsBucket, AggregationsStatsAggregate, SearchResponse } from '@elastic/elasticsearch/lib/api/types';

type BaseValueFilter = string | string[] | number | number[];
type BaseRangeFilter = {
    gte?: number;
    lte?: number;
    gt?: number;
    lt?: number;
};
type FilterFieldValue = BaseValueFilter | BaseRangeFilter;
type Params = {
    size?: number;
    from?: number;
} & Record<string, unknown>;
type SortFields = (Record<string, 'desc' | 'asc'> | '_score')[];
type BoolQuery = {
    must?: Query[];
    filter?: Query[];
    should?: Query[];
    must_not?: Query[];
};
type BaseQueryValue = string | number | boolean;
type MatchQuery = {
    [field: string]: {
        query: BaseQueryValue;
    };
};
type TermQuery = {
    [field: string]: BaseQueryValue;
};
type RangeQuery = {
    [field: string]: BaseRangeFilter;
};
type NestedQuery = {
    path: string;
    query: Query;
};
type Query = {
    bool?: BoolQuery;
    match?: MatchQuery;
    term?: TermQuery;
    range?: RangeQuery;
    nested?: NestedQuery;
    geo_bounding_box?: GeoBoundingBoxQuery;
    geo_distance?: GeoDistanceQuery;
};
type GeoBoundingBoxQuery = {
    [field: string]: {
        top_left: {
            lat: number;
            lon: number;
        };
        bottom_right: {
            lat: number;
            lon: number;
        };
    };
};
type GeoDistanceQuery = {
    distance: string;
} & {
    [field: string]: {
        lat: number;
        lon: number;
    };
};
type Aggregations = {
    stats?: {
        field: SearchRequest['aggs'][string]['stats']['field'];
    };
    terms?: {
        field: SearchRequest['aggs'][string]['terms']['field'];
        order?: SearchRequest['aggs'][string]['terms']['order'];
        size?: SearchRequest['aggs'][string]['terms']['size'];
    };
};
type RequestParams = {
    _es_aggs?: {
        [name: string]: Aggregations;
    };
    _es_filters?: Query;
    _es_sort_fields?: SortFields;
    query?: SearchRequest['query']['query_string']['query'];
    highlight_fields?: SearchRequest['highlight'];
    from?: number;
    size?: number;
};
type ResponseTermsFacet = {
    name: string;
    entries: {
        value: AggregationsSignificantStringTermsBucket['key'];
        count: AggregationsSignificantStringTermsBucket['doc_count'];
    }[];
};
type ResponseStatsFacet = {
    name: string;
    stats: Pick<AggregationsStatsAggregate, 'min' | 'max' | 'avg' | 'sum' | 'count'>;
};
type ResponseFacets = ResponseTermsFacet | ResponseStatsFacet;

interface Options {
    cacheExpiration?: number;
    cache?: boolean;
    headers?: HeadersInit;
}
declare class API {
    private readonly apiKey;
    private readonly endpoint;
    private readonly path;
    private readonly options;
    private readonly cacheService;
    constructor(apiKey: string, endpoint: string, path: string, options?: Options);
    private request;
    post<R extends SearchResponse = SearchResponse>(body: {
        params: RequestParams;
    }): Promise<R>;
    private handleError;
}

interface BaseFacetConfiguration {
    type: 'terms' | 'stats';
    field: string;
    disjunctive?: boolean;
}
interface TermsFacetConfiguration extends BaseFacetConfiguration {
    type: 'terms';
    size: number;
}
interface StatsFacetConfiguration extends BaseFacetConfiguration {
    type: 'stats';
}
type FacetConfiguration = TermsFacetConfiguration | StatsFacetConfiguration;

declare class QueryBuilder {
    private readonly apiClient;
    readonly facets: Record<string, FacetConfiguration>;
    facetFilters: Record<string, FilterFieldValue[]>;
    sort: SortFields;
    filter: Query;
    params: Params;
    constructor(apiClient: API, baseParams?: {
        facets?: Record<string, FacetConfiguration>;
    } & Params);
    /**
     * @public
     * @param {string} field
     * @param {string | Array.string | Object} value
     * @returns {QueryBuilder}
     */
    addFacetFilter(field: string, value: FilterFieldValue): this;
    /**
     * @public
     * @param {string} parameter
     * @param {*} value
     * @returns {QueryBuilder}
     */
    addParameter(parameter: keyof Params, value: Params[keyof Params]): this;
    /**
     * @public
     * @param {string} query
     * @returns {QueryBuilder}
     */
    query(query: string): this;
    /**
     * @async
     * @public - returns search results
     * @returns {Promise.<Array.<Object>>}
     */
    search<Result = unknown>(): Promise<SearchResponse<Result> & {
        facets?: ResponseFacets[];
    }>;
    /**
     * @public
     * @param {Object} value
     * @returns {QueryBuilder}
     */
    setFilter(value: Query): this;
    /**
     * @public
     * @param {number} value
     * @returns {QueryBuilder}
     */
    setFrom(value: Params['from']): this;
    /**
     * @public
     * @param {number} value
     * @returns {QueryBuilder}
     */
    setPageSize(value: Params['size']): this;
    /**
     * @public
     * @param {Array.<Object<string, 'desc' | 'asc'>|'_score'>} sort
     * @returns {QueryBuilder}
     */
    setSort(sort: SortFields): this;
}

declare const Highlight: (hit: SearchResponse['hits']['hits'][0], field: string) => any;

/**
 * @function SearchApplicationClient
 * @param {string} applicationName
 * @param {string} endpoint
 * @param {string} apiKey
 * @param {Object} params
 * @param {Object} apiOptions
 * @returns {function(): QueryBuilder}
 */
declare function SearchApplicationClient(applicationName: string, endpoint: string, apiKey: string, params?: Record<string, any>, apiOptions?: Options): () => QueryBuilder;

export { Highlight, SearchApplicationClient as default };
