import { isNullOrUndefined } from '@coveo/bueno';
import { isErrorResponse, isSuccessResponse, } from '../../api/search/search-api-client.js';
import { applyDidYouMeanCorrection } from '../did-you-mean/did-you-mean-actions.js';
import { snapshot } from '../history/history-actions.js';
import { extractHistory } from '../history/history-state.js';
import { updateQuery } from '../query/query-actions.js';
import { mapSearchResponse, } from '../search/search-mappings.js';
import { logQueryError } from './insight-search-analytics-actions.js';
import { buildInsightSearchRequest } from './insight-search-request.js';
export class AsyncInsightSearchThunkProcessor {
    config;
    onUpdateQueryForCorrection;
    constructor(config, onUpdateQueryForCorrection = (modification) => {
        this.dispatch(updateQuery({ q: modification }));
    }) {
        this.config = config;
        this.onUpdateQueryForCorrection = onUpdateQueryForCorrection;
    }
    async fetchFromAPI({ request, mappings }, options) {
        const startedAt = Date.now();
        const response = mapSearchResponse(await this.extra.apiClient.query(request, options), mappings);
        const duration = Date.now() - startedAt;
        const queryExecuted = this.getState().query?.q || '';
        return {
            response,
            duration,
            queryExecuted,
            requestExecuted: request,
        };
    }
    async process(fetched) {
        return (this.processQueryErrorOrContinue(fetched) ??
            (await this.processQueryCorrectionsOrContinue(fetched)) ??
            this.processSuccessResponse(fetched));
    }
    processQueryErrorOrContinue(fetched) {
        if (isErrorResponse(fetched.response)) {
            this.dispatch(logQueryError(fetched.response.error));
            return this.rejectWithValue(fetched.response.error);
        }
        return null;
    }
    async processQueryCorrectionsOrContinue(fetched) {
        const state = this.getState();
        const successResponse = this.getSuccessResponse(fetched);
        if (!successResponse || !state.didYouMean) {
            return null;
        }
        const { enableDidYouMean, automaticallyCorrectQuery } = state.didYouMean;
        const { results, queryCorrections, queryCorrection } = successResponse;
        if (!enableDidYouMean) {
            return null;
        }
        if (!automaticallyCorrectQuery) {
            if (!isNullOrUndefined(queryCorrection) &&
                !isNullOrUndefined(queryCorrection.correctedQuery)) {
                return this.processNextDidYouMeanAutoCorrection(fetched);
            }
            return null;
        }
        const shouldExecuteLegacyDidYouMeanAutoCorrection = results.length === 0 && queryCorrections && queryCorrections.length !== 0;
        const shouldExecuteNextDidYouMeanAutoCorrection = !isNullOrUndefined(queryCorrection) &&
            !isNullOrUndefined(queryCorrection.correctedQuery);
        const shouldExitWithNoAutoCorrection = !shouldExecuteLegacyDidYouMeanAutoCorrection &&
            !shouldExecuteNextDidYouMeanAutoCorrection;
        if (shouldExitWithNoAutoCorrection) {
            return null;
        }
        const processedDidYouMean = shouldExecuteLegacyDidYouMeanAutoCorrection
            ? await this.processLegacyDidYouMeanAutoCorrection(fetched)
            : this.processNextDidYouMeanAutoCorrection(fetched);
        this.dispatch(snapshot(extractHistory(this.getState())));
        return processedDidYouMean;
    }
    async processLegacyDidYouMeanAutoCorrection(originalFetchedResponse) {
        const originalQuery = this.getCurrentQuery();
        const originalSearchSuccessResponse = this.getSuccessResponse(originalFetchedResponse);
        if (!originalSearchSuccessResponse.queryCorrections) {
            return null;
        }
        const { correctedQuery } = originalSearchSuccessResponse.queryCorrections[0];
        const retried = await this.automaticallyRetryQueryWithCorrection(correctedQuery);
        if (isErrorResponse(retried.response)) {
            this.dispatch(logQueryError(retried.response.error));
            return this.rejectWithValue(retried.response.error);
        }
        this.dispatch(snapshot(extractHistory(this.getState())));
        return {
            ...retried,
            response: {
                ...retried.response.success,
                queryCorrections: originalSearchSuccessResponse.queryCorrections,
            },
            automaticallyCorrected: true,
            originalQuery,
        };
    }
    processNextDidYouMeanAutoCorrection(originalFetchedResponse) {
        const successResponse = this.getSuccessResponse(originalFetchedResponse);
        const { correctedQuery, originalQuery } = successResponse.queryCorrection;
        this.onUpdateQueryForCorrection(correctedQuery);
        return {
            ...originalFetchedResponse,
            response: {
                ...successResponse,
            },
            queryExecuted: correctedQuery,
            automaticallyCorrected: true,
            originalQuery,
        };
    }
    async automaticallyRetryQueryWithCorrection(correction) {
        this.onUpdateQueryForCorrection(correction);
        const state = this.getState();
        const fetched = await this.fetchFromAPI(await buildInsightSearchRequest(state));
        this.dispatch(applyDidYouMeanCorrection(correction));
        return fetched;
    }
    processSuccessResponse(fetched) {
        this.dispatch(snapshot(extractHistory(this.getState())));
        return {
            ...fetched,
            response: this.getSuccessResponse(fetched),
            automaticallyCorrected: false,
            originalQuery: this.getCurrentQuery(),
        };
    }
    getSuccessResponse(fetched) {
        if (isSuccessResponse(fetched.response)) {
            return fetched.response.success;
        }
        return null;
    }
    get extra() {
        return this.config.extra;
    }
    getState() {
        return this.config.getState();
    }
    get dispatch() {
        return this.config.dispatch;
    }
    get rejectWithValue() {
        return this.config.rejectWithValue;
    }
    getCurrentQuery() {
        const state = this.getState();
        return state.query?.q !== undefined ? state.query.q : '';
    }
}
