import { RecordValue, Schema } from '@coveo/bueno';
import { findActiveValueAncestry } from '../../../features/facets/category-facet-set/category-facet-utils.js';
import { getFacets, getQ, getSortCriteria, getTab, } from '../../../features/parameter-manager/parameter-manager-selectors.js';
import { getQueryInitialState } from '../../../features/query/query-state.js';
import { restoreSearchParameters, } from '../../../features/search-parameters/search-parameter-actions.js';
import { searchParametersDefinition } from '../../../features/search-parameters/search-parameter-schema.js';
import { initialSearchParameterSelector } from '../../../features/search-parameters/search-parameter-selectors.js';
import { getSortCriteriaInitialState } from '../../../features/sort-criteria/sort-criteria-state.js';
import { validateInitialState } from '../../../utils/validate-payload.js';
import { buildController, } from '../../controller/headless-controller.js';
const initialStateSchema = new Schema({
    parameters: new RecordValue({
        options: { required: true },
        values: searchParametersDefinition,
    }),
});
/**
 * Creates a `SearchParameterManager` controller instance.
 *
 * @param engine - The headless engine.
 * @param props - The configurable `SearchParameterManager` properties.
 * @returns A `SearchParameterManager` controller instance.
 */
export function buildCoreSearchParameterManager(engine, props) {
    const { dispatch } = engine;
    const controller = buildController(engine);
    validateInitialState(engine, initialStateSchema, props.initialState, 'buildSearchParameterManager');
    const parametersWithValidTab = ensureTabIsValid(engine.state.tabSet, props.initialState.parameters);
    dispatch(restoreSearchParameters(parametersWithValidTab));
    return {
        ...controller,
        synchronize(parameters) {
            const enrichedParametersWithValidTab = ensureTabIsValid(engine.state.tabSet, enrichParameters(engine, parameters));
            dispatch(restoreSearchParameters(enrichedParametersWithValidTab));
        },
        get state() {
            const parameters = getCoreActiveSearchParameters(engine);
            return { parameters };
        },
    };
}
export function enrichParameters(engine, parameters) {
    return {
        ...initialSearchParameterSelector(engine.state),
        ...parameters,
    };
}
/**
 * This function is essential to ensure that the `tab` parameter is valid.
 * We need it to be valid prior to dispatching the `restoreSearchParameters` action since the facet logic relies on the `tab` parameter to determine which facets to show.
 * If the `tab` parameter is invalid, it can lead to unexpected behavior in the facets.
 */
function ensureTabIsValid(tabSet, parameters) {
    if (parameters.tab && tabSet) {
        const tabExists = Object.values(tabSet).some((tab) => tab.id === parameters.tab);
        const currentActiveTab = Object.values(tabSet).find((tab) => tab.isActive);
        if (!tabExists && currentActiveTab) {
            return { ...parameters, tab: currentActiveTab.id };
        }
        else if (!tabExists) {
            return { ...parameters, tab: '' };
        }
    }
    return parameters;
}
export function getCoreActiveSearchParameters(engine) {
    const state = engine.state;
    return {
        ...getQ(state.query, (s) => s.q, getQueryInitialState().q),
        ...getTab(state.tabSet, (tabSet) => {
            const activeTab = Object.values(tabSet ?? {}).find((tab) => tab.isActive);
            return activeTab ? activeTab.id : Object.keys(tabSet ?? {})[0];
        }, state.tabSet ? Object.keys(state.tabSet)[0] : ''),
        ...getSortCriteria(state.sortCriteria, (sortCriteria) => sortCriteria, getSortCriteriaInitialState()),
        ...getFacets(state.facetSet, facetIsEnabled(state), getSelectedValues, 'f'),
        ...getFacets(state.facetSet, facetIsEnabled(state), getExcludedValues, 'fExcluded'),
        ...getCategoryFacets(state),
        ...getNumericFacets(state),
        ...getDateFacets(state),
        ...getAutomaticFacets(state),
    };
}
function facetIsEnabled(state) {
    return (facetId) => {
        return state.facetOptions?.facets[facetId]?.enabled ?? true;
    };
}
export function getSelectedValues(request) {
    return request.currentValues
        .filter((fv) => fv.state === 'selected')
        .map((fv) => fv.value);
}
export function getSelectedRangeValues(request) {
    return request.currentValues.filter((fv) => fv.state === 'selected');
}
function getExcludedValues(request) {
    return request.currentValues
        .filter((fv) => fv.state === 'excluded')
        .map((fv) => fv.value);
}
function getCategoryFacets(state) {
    return getFacets(state.categoryFacetSet, facetIsEnabled(state), (request) => findActiveValueAncestry(request.currentValues).map((v) => v.value), 'cf');
}
function getNumericFacets(state) {
    return getFacets(state.numericFacetSet, facetIsEnabled(state), getSelectedRangeValues, 'nf');
}
function getDateFacets(state) {
    return getFacets(state.dateFacetSet, facetIsEnabled(state), getSelectedRangeValues, 'df');
}
function getAutomaticFacets(state) {
    const set = state.automaticFacetSet?.set;
    if (set === undefined) {
        return {};
    }
    const af = Object.entries(set)
        .map(([facetId, { response }]) => {
        const selectedValues = getSelectedResponseValues(response);
        return selectedValues.length ? { [facetId]: selectedValues } : {};
    })
        // biome-ignore lint/performance/noAccumulatingSpread: <>
        .reduce((acc, obj) => ({ ...acc, ...obj }), {});
    return Object.keys(af).length ? { af } : {};
}
function getSelectedResponseValues(response) {
    return response.values
        .filter((fv) => fv.state === 'selected')
        .map((fv) => fv.value);
}
