import { createWaitForActionMiddleware } from '../../utils/utils.js';
import { buildLogger } from '../logger.js';
import { augmentPreprocessRequestWithForwardedFor } from '../ssr-engine/augment-preprocess-request.js';
import { buildControllerDefinitions, composeFunction, createStaticState, } from '../ssr-engine/common.js';
import { buildSearchEngine, } from './search-engine.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 = composeFunction(async (...params) => {
        const buildResult = await build(...params);
        const staticState = await fetchStaticState.fromBuildResult({
            buildResult,
        });
        options.configuration.preprocessRequest =
            augmentPreprocessRequestWithForwardedFor({
                preprocessRequest: options.configuration.preprocessRequest,
                navigatorContextProvider: options.navigatorContextProvider,
                loggerOptions: options.loggerOptions,
            });
        return staticState;
    }, {
        fromBuildResult: async (...params) => {
            const [{ buildResult: { engine, controllers }, },] = params;
            engine.executeFirstSearch();
            return createStaticState({
                searchAction: await engine.waitForSearchCompletedAction(),
                controllers,
            });
        },
    });
    const hydrateStaticState = composeFunction(async (...params) => {
        const buildResult = await build(...params);
        const staticState = await hydrateStaticState.fromBuildResult({
            buildResult,
            searchAction: params[0].searchAction,
        });
        return staticState;
    }, {
        fromBuildResult: async (...params) => {
            const [{ buildResult: { engine, controllers }, searchAction, },] = params;
            engine.dispatch(searchAction);
            await engine.waitForSearchCompletedAction();
            return { engine, controllers };
        },
    });
    return {
        build,
        fetchStaticState,
        hydrateStaticState,
        setNavigatorContextProvider,
    };
}
