import { BooleanValue, NumberValue, StringValue } from '@coveo/bueno';
import { createAction, createAsyncThunk } from '@reduxjs/toolkit';
import HistoryStore from '../../api/analytics/coveo.analytics/history-store.js';
import { requiredNonEmptyString, validatePayload, } from '../../utils/validate-payload.js';
import { makeBasicNewSearchAnalyticsAction, } from '../analytics/analytics-utils.js';
import { SearchPageEvents } from '../analytics/search-action-cause.js';
import { deselectAllBreadcrumbs, deselectAllNonBreadcrumbs, } from '../breadcrumb/breadcrumb-actions.js';
import { updateFacetAutoSelection } from '../facets/generic/facet-actions.js';
import { searchboxAsYouType } from '../instant-results/instant-result-analytics-actions.js';
import { updateInstantResultsQuery, } from '../instant-results/instant-results-actions.js';
import { updatePage } from '../pagination/pagination-actions.js';
import { updateQuery, } from '../query/query-actions.js';
import { buildSearchAndFoldingLoadCollectionRequest } from '../search-and-folding/search-and-folding-request.js';
import { legacyExecuteSearch, legacyFetchInstantResults, legacyFetchMoreResults, legacyFetchPage, } from './legacy/search-actions.js';
import { AsyncSearchThunkProcessor, } from './search-actions-thunk-processor.js';
import { mapSearchRequest } from './search-mappings.js';
import { buildSearchRequest } from './search-request.js';
export const prepareForSearchWithQuery = createAsyncThunk('search/prepareForSearchWithQuery', (payload, thunk) => {
    const { dispatch } = thunk;
    validatePayload(payload, {
        q: new StringValue(),
        enableQuerySyntax: new BooleanValue(),
        clearFilters: new BooleanValue(),
    });
    if (payload.clearFilters) {
        dispatch(deselectAllBreadcrumbs());
        dispatch(deselectAllNonBreadcrumbs());
    }
    dispatch(updateFacetAutoSelection({ allow: true }));
    dispatch(updateQuery({ q: payload.q, enableQuerySyntax: payload.enableQuerySyntax }));
    dispatch(updatePage(1));
});
export const updateSearchAction = createAction('search/updateSearchAction');
export const executeSearch = createAsyncThunk('search/executeSearch', async (searchAction, config) => {
    const state = config.getState();
    if (state.configuration.analytics.analyticsMode === 'legacy') {
        return legacyExecuteSearch(state, config, searchAction.legacy);
    }
    addEntryInActionsHistory(state);
    const analyticsAction = searchAction.next
        ? buildSearchReduxAction(searchAction.next)
        : undefined;
    config.dispatch(updateSearchAction(searchAction.next));
    const request = await buildSearchRequest(state, config.extra.navigatorContext, analyticsAction);
    const processor = new AsyncSearchThunkProcessor({ ...config, analyticsAction: analyticsAction ?? {} });
    const fetched = await processor.fetchFromAPI(request, {
        origin: 'mainSearch',
    });
    return await processor.process(fetched);
});
export const fetchPage = createAsyncThunk('search/fetchPage', async (searchAction, config) => {
    const state = config.getState();
    addEntryInActionsHistory(state);
    if (state.configuration.analytics.analyticsMode === 'legacy' ||
        !searchAction.next) {
        return legacyFetchPage(state, config, searchAction.legacy);
    }
    const processor = new AsyncSearchThunkProcessor({
        ...config,
        analyticsAction: searchAction.next,
    });
    const request = await buildSearchRequest(state, config.extra.navigatorContext, searchAction.next);
    const fetched = await processor.fetchFromAPI(request, { origin: 'mainSearch' });
    return await processor.process(fetched);
});
export const fetchMoreResults = createAsyncThunk('search/fetchMoreResults', async (_, config) => {
    const state = config.getState();
    if (state.configuration.analytics.analyticsMode === 'legacy') {
        return legacyFetchMoreResults(config, state);
    }
    const analyticsAction = makeBasicNewSearchAnalyticsAction(SearchPageEvents.browseResults, config.getState);
    const processor = new AsyncSearchThunkProcessor({
        ...config,
        analyticsAction,
    });
    const request = await buildFetchMoreRequest(state, config.extra.navigatorContext, analyticsAction);
    const fetched = await processor.fetchFromAPI(request, { origin: 'mainSearch' });
    return await processor.process(fetched);
});
export const fetchFacetValues = createAsyncThunk('search/fetchFacetValues', async (searchAction, config) => {
    const state = config.getState();
    if (state.configuration.analytics.analyticsMode === 'legacy') {
        return legacyExecuteSearch(state, config, searchAction.legacy);
    }
    const processor = new AsyncSearchThunkProcessor({ ...config, analyticsAction: {} });
    const request = await buildFetchFacetValuesRequest(state, config.extra.navigatorContext);
    const fetched = await processor.fetchFromAPI(request, {
        origin: 'facetValues',
    });
    return await processor.process(fetched);
});
export const fetchInstantResults = createAsyncThunk('search/fetchInstantResults', async (payload, config) => {
    const state = config.getState();
    if (state.configuration.analytics.analyticsMode === 'legacy') {
        return legacyFetchInstantResults(payload, config);
    }
    validatePayload(payload, {
        id: requiredNonEmptyString,
        q: requiredNonEmptyString,
        maxResultsPerQuery: new NumberValue({
            required: true,
            min: 1,
        }),
        cacheTimeout: new NumberValue(),
    });
    const { q, maxResultsPerQuery } = payload;
    const analyticsAction = buildSearchReduxAction(searchboxAsYouType());
    const request = await buildInstantResultSearchRequest(state, config.extra.navigatorContext, q, maxResultsPerQuery, analyticsAction);
    const processor = new AsyncSearchThunkProcessor({ ...config, analyticsAction }, (modification) => {
        config.dispatch(updateInstantResultsQuery({ q: modification, id: payload.id }));
    });
    const fetched = await processor.fetchFromAPI(request, {
        origin: 'instantResults',
        disableAbortWarning: true,
    });
    const processed = await processor.process(fetched);
    if ('response' in processed) {
        return {
            results: processed.response.results,
            searchUid: processed.response.searchUid,
            totalCountFiltered: processed.response.totalCountFiltered,
            duration: processed.duration,
        };
    }
    return processed;
});
const buildFetchMoreRequest = async (state, navigatorContext, eventDescription) => {
    const mappedRequest = await buildSearchRequest(state, navigatorContext, eventDescription);
    mappedRequest.request = {
        ...mappedRequest.request,
        firstResult: (state.pagination?.firstResult ?? 0) +
            (state.search?.results.length ?? 0),
    };
    return mappedRequest;
};
const buildInstantResultSearchRequest = async (state, navigatorContext, q, numberOfResults, eventDescription) => {
    const sharedWithFoldingRequest = buildSearchAndFoldingLoadCollectionRequest(state, navigatorContext, eventDescription);
    return mapSearchRequest({
        ...sharedWithFoldingRequest,
        ...(state.didYouMean && {
            enableDidYouMean: state.didYouMean.enableDidYouMean,
        }),
        numberOfResults,
        q,
    });
};
const buildFetchFacetValuesRequest = async (state, navigatorContext, eventDescription) => {
    const mappedRequest = await buildSearchRequest(state, navigatorContext, eventDescription);
    // Specifying a numberOfResults of 0 will not log the query as a full fledged query in the API
    // it will also alleviate the load on the index
    mappedRequest.request.numberOfResults = 0;
    return mappedRequest;
};
const addEntryInActionsHistory = (state) => {
    if (state.configuration.analytics.enabled) {
        HistoryStore.getInstance().addElement({
            name: 'Query',
            ...(state.query?.q && {
                value: state.query.q,
            }),
            time: JSON.stringify(new Date()),
        });
    }
};
const buildSearchReduxAction = (action) => ({
    actionCause: action.actionCause,
    type: action.actionCause,
});
