import { buildLogger } from '../../../app/logger.js';
import { buildSearchEngine, } from '../../../app/search-engine/search-engine.js';
import { createWaitForActionMiddleware } from '../../../utils/utils.js';
import { augmentPreprocessRequestWithForwardedFor } from '../../common/augment-preprocess-request.js';
import { buildControllerDefinitions, createStaticState, } from '../../common/controller-utils.js';
function isSearchCompletedAction(action) {
    return /^search\/executeSearch\/(fulfilled|rejected)$/.test(action.type);
}
function buildSSRSearchEngine(options) {
    const { middleware, promise } = createWaitForActionMiddleware(isSearchCompletedAction);
    const searchEngine = buildSearchEngine({
        ...options,
        middlewares: [...(options.middlewares ?? []), middleware],
    });
    return {
        ...searchEngine,
        get state() {
            return searchEngine.state;
        },
        waitForSearchCompletedAction() {
            return promise;
        },
    };
}
/**
 * Initializes a Search engine definition in SSR with given controllers definitions and search engine config.
 *
 * @param options - The search engine definition
 * @returns Three utility functions to fetch the initial state of the engine in SSR, hydrate the state in CSR,
 *  and a build function that can be used for edge cases requiring more control.
 *
 * @group Engine
 */
export function defineSearchEngine(options) {
    const { controllers: controllerDefinitions, ...engineOptions } = options;
    const getOptions = () => {
        return engineOptions;
    };
    const setNavigatorContextProvider = (navigatorContextProvider) => {
        engineOptions.navigatorContextProvider = navigatorContextProvider;
    };
    const build = async (...[buildOptions]) => {
        const logger = buildLogger(options.loggerOptions);
        if (!getOptions().navigatorContextProvider) {
            logger.warn('[WARNING] Missing navigator context in server-side code. Make sure to set it with `setNavigatorContextProvider` before calling fetchStaticState()');
        }
        const engine = buildSSRSearchEngine(buildOptions?.extend
            ? await buildOptions.extend(getOptions())
            : getOptions());
        const controllers = buildControllerDefinitions({
            definitionsMap: (controllerDefinitions ?? {}),
            engine,
            propsMap: (buildOptions && 'controllers' in buildOptions
                ? buildOptions.controllers
                : {}),
        });
        return {
            engine,
            controllers,
        };
    };
    const fetchStaticState = async (...params) => {
        const { engine, controllers } = await build(...params);
        options.configuration.preprocessRequest =
            augmentPreprocessRequestWithForwardedFor({
                preprocessRequest: options.configuration.preprocessRequest,
                navigatorContextProvider: options.navigatorContextProvider,
                loggerOptions: options.loggerOptions,
            });
        engine.executeFirstSearch();
        const staticState = createStaticState({
            searchAction: await engine.waitForSearchCompletedAction(),
            controllers: controllers,
        });
        return staticState;
    };
    const hydrateStaticState = async (...params) => {
        const { engine, controllers } = await build(...params);
        engine.dispatch(params[0].searchAction);
        await engine.waitForSearchCompletedAction();
        return { engine, controllers };
    };
    return {
        fetchStaticState,
        hydrateStaticState,
        setNavigatorContextProvider,
    };
}
