import { sortCriteriaMap, sortFacets } from '../../utils/facet-utils.js';
import { getFacetRequests, } from '../facets/generic/interfaces/generic-facet-request.js';
import { maximumNumberOfResultsFromIndex } from '../pagination/pagination-constants.js';
import { buildSearchAndFoldingLoadCollectionRequest as legacyBuildSearchAndFoldingLoadCollectionRequest } from '../search-and-folding/legacy/search-and-folding-request.js';
import { buildSearchAndFoldingLoadCollectionRequest } from '../search-and-folding/search-and-folding-request.js';
import { selectStaticFilterExpressions } from '../static-filter-set/static-filter-set-selectors.js';
import { mapSearchRequest } from './search-mappings.js';
export const buildSearchRequest = async (state, navigatorContext, eventDescription) => {
    const cq = buildConstantQuery(state);
    const facets = getFacets(state);
    const automaticFacets = getAutomaticFacets(state);
    const sharedWithFoldingRequest = state.configuration.analytics.analyticsMode === 'legacy'
        ? await legacyBuildSearchAndFoldingLoadCollectionRequest(state, eventDescription)
        : buildSearchAndFoldingLoadCollectionRequest(state, navigatorContext, eventDescription);
    // Corner case:
    // If the number of results requested would go over the index limit (maximumNumberOfResultsFromIndex)
    // we need to request fewer results in order to ensure we do not receive an exception from the index
    const getNumberOfResultsWithinIndexLimit = () => {
        if (!state.pagination) {
            return undefined;
        }
        const isOverIndexLimit = state.pagination.firstResult + state.pagination.numberOfResults >
            maximumNumberOfResultsFromIndex;
        if (isOverIndexLimit) {
            return maximumNumberOfResultsFromIndex - state.pagination.firstResult;
        }
        return state.pagination.numberOfResults;
    };
    return mapSearchRequest({
        ...sharedWithFoldingRequest,
        ...(state.didYouMean && {
            queryCorrection: {
                enabled: state.didYouMean.enableDidYouMean &&
                    state.didYouMean.queryCorrectionMode === 'next',
                options: {
                    automaticallyCorrect: state.didYouMean.automaticallyCorrectQuery
                        ? 'whenNoResults'
                        : 'never',
                },
            },
            enableDidYouMean: state.didYouMean.enableDidYouMean &&
                state.didYouMean.queryCorrectionMode === 'legacy',
        }),
        ...(cq && { cq }),
        ...(facets.length && { facets }),
        ...(state.pagination && {
            numberOfResults: getNumberOfResultsWithinIndexLimit(),
            firstResult: state.pagination.firstResult,
        }),
        ...(state.facetOptions && {
            facetOptions: { freezeFacetOrder: state.facetOptions.freezeFacetOrder },
        }),
        ...(state.folding?.enabled && {
            filterField: state.folding.fields.collection,
            childField: state.folding.fields.parent,
            parentField: state.folding.fields.child,
            filterFieldRange: state.folding.filterFieldRange,
        }),
        ...(state.automaticFacetSet && {
            generateAutomaticFacets: {
                desiredCount: state.automaticFacetSet.desiredCount,
                numberOfValues: state.automaticFacetSet.numberOfValues,
                currentFacets: automaticFacets,
            },
        }),
        ...(state.generatedAnswer && {
            pipelineRuleParameters: {
                mlGenerativeQuestionAnswering: {
                    responseFormat: state.generatedAnswer.responseFormat,
                    citationsFieldToInclude: state.generatedAnswer.fieldsToIncludeInCitations,
                },
            },
        }),
    });
};
function getFacets(state) {
    return sortFacets(getAllEnabledFacets(state), state.facetOrder ?? []);
}
function getAutomaticFacets(state) {
    const facets = state.automaticFacetSet?.set;
    return facets
        ? Object.values(facets)
            .map((facet) => facet.response)
            .map(responseToAutomaticFacetRequest)
            .filter((facetRequest) => facetRequest.currentValues.length > 0)
        : undefined;
}
function responseToAutomaticFacetRequest(response) {
    const { field, label, values } = response;
    const selectedValues = values.filter((value) => value.state === 'selected');
    return {
        field,
        label,
        currentValues: selectedValues,
    };
}
function getAllEnabledFacets(state) {
    return getAllFacets(state).filter(({ facetId }) => state.facetOptions?.facets[facetId]?.enabled ?? true);
}
function getAllFacets(state) {
    return [
        ...getSpecificFacetRequests(state.facetSet ?? {}),
        ...getRangeFacetRequests(state.numericFacetSet ?? {}),
        ...getRangeFacetRequests(state.dateFacetSet ?? {}),
        ...getFacetRequests(state.categoryFacetSet ?? {}),
    ];
}
function getSpecificFacetRequests(state) {
    return getFacetRequests(state).map((request) => {
        /* The Search API does not support 'alphanumericDescending' as a string value and instead relies on a new sort mechanism to specify sort order.
        At the moment, this is only supported for alphanumeric sorting, but will likely transition to this pattern for other types in the future. */
        const sortCriteria = sortCriteriaMap[request.sortCriteria];
        if (sortCriteria) {
            return {
                ...request,
                sortCriteria,
            };
        }
        return request;
    });
}
function getRangeFacetRequests(state) {
    return getFacetRequests(state).map((request) => {
        const currentValues = request.currentValues;
        const hasActiveValues = currentValues.some(({ state }) => state !== 'idle');
        const hasPreviousStateValues = currentValues.some((value) => value.previousState);
        if (request.generateAutomaticRanges &&
            !hasActiveValues &&
            !hasPreviousStateValues) {
            return { ...request, currentValues: [] };
        }
        return request;
    });
}
function buildConstantQuery(state) {
    const cq = state.advancedSearchQueries?.cq.trim() || '';
    const activeTab = Object.values(state.tabSet || {}).find((tab) => tab.isActive);
    const tabExpression = activeTab?.expression.trim() || '';
    const filterExpressions = selectStaticFilterExpressions(state);
    return [cq, tabExpression, ...filterExpressions]
        .filter((expression) => !!expression)
        .join(' AND ');
}
