/** biome-ignore-all lint/suspicious/noConfusingVoidType: <> */
import { isNullOrUndefined, RecordValue, Schema, StringValue, } from '@coveo/bueno';
import { createAsyncThunk, } from '@reduxjs/toolkit';
import { getRelayInstanceFromState } from '../../api/analytics/analytics-relay-client.js';
import { CaseAssistAnalyticsProvider, configureCaseAssistAnalytics, } from '../../api/analytics/case-assist-analytics.js';
import { configureInsightAnalytics, InsightAnalyticsProvider, } from '../../api/analytics/insight-analytics.js';
import { configureLegacyAnalytics, SearchAnalyticsProvider, } from '../../api/analytics/search-analytics.js';
import { requiredNonEmptyString } from '../../utils/validate-payload.js';
import { getAllIncludedResultsFrom } from '../folding/folding-utils.js';
import { getPipelineInitialState } from '../pipeline/pipeline-state.js';
export function makeBasicNewSearchAnalyticsAction(actionCause, getState) {
    return {
        ...new SearchAnalyticsProvider(getState).getBaseMetadata(),
        actionCause,
        type: actionCause,
    };
}
function makeInstantlyCallable(action) {
    return Object.assign(action, { instantlyCallable: true });
}
function makePreparableAnalyticsAction(prefix, buildEvent) {
    const createAnalyticsAction = (body) => makeInstantlyCallable(createAsyncThunk(prefix, body));
    const rootAction = createAnalyticsAction(async (_, { getState, extra }) => {
        const { analyticsClientMiddleware, preprocessRequest, logger } = extra;
        return await (await buildEvent({
            getState,
            analyticsClientMiddleware,
            preprocessRequest,
            logger,
        })).log({ state: getState(), extra });
    });
    const prepare = async ({ getState, analyticsClientMiddleware, preprocessRequest, logger, }) => {
        const { description, log } = await buildEvent({
            getState,
            analyticsClientMiddleware,
            preprocessRequest,
            logger,
        });
        return {
            description,
            action: createAnalyticsAction(async (_, { getState: getNewState, extra: newExtra }) => {
                return await log({ state: getNewState(), extra: newExtra });
            }),
        };
    };
    Object.assign(rootAction, {
        prepare,
    });
    return rootAction;
}
const makeAnalyticsActionFactory = (configurator, legacyGetBuilderConverter, providerClass) => {
    function makeAnalyticsAction(...params) {
        const options = params.length === 1
            ? {
                ...params[0],
                __legacy__getBuilder: legacyGetBuilderConverter(params[0].__legacy__getBuilder),
                analyticsConfigurator: configurator,
                providerClass: providerClass,
            }
            : {
                prefix: params[0],
                __legacy__getBuilder: legacyGetBuilderConverter(params[1]),
                __legacy__provider: params[2],
                analyticsConfigurator: configurator,
                providerClass: providerClass,
            };
        return internalMakeAnalyticsAction(options);
    }
    return makeAnalyticsAction;
};
const shouldSendLegacyEvent = (state) => state.configuration.analytics.analyticsMode === 'legacy';
const shouldSendNextEvent = (state) => state.configuration.analytics.analyticsMode === 'next';
const internalMakeAnalyticsAction = ({ prefix, __legacy__getBuilder, __legacy__provider, analyticsPayloadBuilder, analyticsType, analyticsConfigurator, providerClass, }) => {
    __legacy__provider ??= (getState) => new providerClass(getState);
    return makePreparableAnalyticsAction(prefix, async ({ getState, analyticsClientMiddleware, preprocessRequest, logger, }) => {
        const loggers = [];
        const analyticsAction = {
            log: async ({ state }) => {
                for (const log of loggers) {
                    await log(state);
                }
            },
        };
        const state = getState();
        const client = analyticsConfigurator({
            getState,
            logger,
            analyticsClientMiddleware,
            preprocessRequest,
            provider: __legacy__provider(getState),
        });
        const builder = await __legacy__getBuilder(client, getState());
        analyticsAction.description = builder?.description;
        loggers.push(async (state) => {
            if (shouldSendLegacyEvent(state)) {
                await logLegacyEvent(builder, __legacy__provider, state, logger, client.coveoAnalyticsClient);
            }
        });
        const { emit } = getRelayInstanceFromState(state);
        loggers.push(async (state) => {
            if (shouldSendNextEvent(state) &&
                analyticsType &&
                analyticsPayloadBuilder) {
                const payload = analyticsPayloadBuilder(state);
                await logNextEvent(emit, analyticsType, payload);
            }
        });
        return analyticsAction;
    });
};
async function logLegacyEvent(builder, __legacy__provider, state, logger, client) {
    __legacy__provider(() => state);
    const response = await builder?.log({
        searchUID: __legacy__provider(() => state).getSearchUID(),
    });
    logger.info({ client, response }, 'Analytics response');
}
const fromLogToLegacyBuilderFactory = (actionCause) => {
    const fromLogToLegacyBuilder = (log) => {
        return (client, state) => Promise.resolve({
            description: { actionCause: actionCause },
            log: async (_metadata) => {
                log(client, state);
            },
        });
    };
    return fromLogToLegacyBuilder;
};
export const makeAnalyticsAction = makeAnalyticsActionFactory((options) => configureLegacyAnalytics({
    ...options,
    provider: options.provider || new SearchAnalyticsProvider(options.getState),
}), (original) => original, SearchAnalyticsProvider);
export const makeCaseAssistAnalyticsAction = makeAnalyticsActionFactory(configureCaseAssistAnalytics, fromLogToLegacyBuilderFactory('caseAssist'), CaseAssistAnalyticsProvider);
export const makeInsightAnalyticsActionFactory = (actionCause) => {
    const makeInsightAnalyticsAction = makeAnalyticsActionFactory(configureInsightAnalytics, fromLogToLegacyBuilderFactory(actionCause), InsightAnalyticsProvider);
    return makeInsightAnalyticsAction;
};
export const partialDocumentInformation = (result, state) => {
    const paginationBasedIndex = (index) => index + (state.pagination?.firstResult ?? 0);
    let resultIndex = -1;
    const parentResults = state.search?.results;
    resultIndex = findPositionWithUniqueId(result, parentResults);
    if (resultIndex < 0) {
        resultIndex = findPositionInChildResults(result, parentResults);
    }
    if (resultIndex < 0) {
        // ¯\_(ツ)_/¯
        resultIndex = 0;
    }
    return buildPartialDocumentInformation(result, paginationBasedIndex(resultIndex), state);
};
export const partialRecommendationInformation = (result, state) => {
    const resultIndex = state.recommendation?.recommendations.findIndex(({ uniqueId }) => result.uniqueId === uniqueId) || 0;
    return buildPartialDocumentInformation(result, resultIndex, state);
};
export const partialCitationInformation = (citation, state) => {
    return {
        sourceName: getCitationSourceName(citation),
        documentPosition: 1,
        documentTitle: citation.title,
        documentUri: citation.uri,
        documentUrl: citation.clickUri,
        queryPipeline: state.pipeline || getPipelineInitialState(),
    };
};
function getCitationSourceName(citation) {
    const source = citation.source;
    if (isNullOrUndefined(source)) {
        return 'unknown';
    }
    return source;
}
export const citationDocumentIdentifier = (citation) => {
    return {
        contentIdKey: 'permanentid',
        contentIdValue: citation.permanentid || '',
    };
};
function buildPartialDocumentInformation(result, resultIndex, state) {
    const collection = result.raw.collection;
    const collectionName = typeof collection === 'string' ? collection : 'default';
    return {
        collectionName,
        documentAuthor: getDocumentAuthor(result),
        documentPosition: resultIndex + 1,
        documentTitle: result.title,
        documentUri: result.uri,
        documentUriHash: result.raw.urihash,
        documentUrl: result.clickUri,
        rankingModifier: result.rankingModifier || '',
        sourceName: getSourceName(result),
        queryPipeline: state.pipeline || getPipelineInitialState(),
    };
}
export const documentIdentifier = (result) => {
    if (!result.raw.permanentid) {
        console.warn('Missing field permanentid on result. This might cause many issues with your Coveo deployment. See https://docs.coveo.com/en/1913 and https://docs.coveo.com/en/1640 for more information.', result);
    }
    return {
        contentIDKey: 'permanentid',
        contentIDValue: result.raw.permanentid || '',
    };
};
const rawPartialDefinition = {
    urihash: new StringValue(),
    sourcetype: new StringValue(),
    permanentid: new StringValue(),
};
export const resultPartialDefinition = {
    uniqueId: requiredNonEmptyString,
    raw: new RecordValue({ values: rawPartialDefinition }),
    title: requiredNonEmptyString,
    uri: requiredNonEmptyString,
    clickUri: requiredNonEmptyString,
    rankingModifier: new StringValue({ required: false, emptyAllowed: true }),
};
function partialRawPayload(raw) {
    return Object.assign({}, ...Object.keys(rawPartialDefinition).map((key) => ({ [key]: raw[key] })));
}
function partialResultPayload(result) {
    return Object.assign({}, ...Object.keys(resultPartialDefinition).map((key) => ({
        [key]: result[key],
    })), { raw: partialRawPayload(result.raw) });
}
function getDocumentAuthor(result) {
    const author = result.raw.author;
    if (isNullOrUndefined(author)) {
        return 'unknown';
    }
    return Array.isArray(author) ? author.join(';') : `${author}`;
}
function getSourceName(result) {
    const source = result.raw.source;
    if (isNullOrUndefined(source)) {
        return 'unknown';
    }
    return source;
}
export const validateResultPayload = (result) => new Schema(resultPartialDefinition).validate(partialResultPayload(result));
function findPositionInChildResults(targetResult, parentResults) {
    for (const [i, parent] of parentResults.entries()) {
        const children = getAllIncludedResultsFrom(parent);
        const childIndex = findPositionWithUniqueId(targetResult, children);
        if (childIndex !== -1) {
            return i;
        }
    }
    return -1;
}
function findPositionWithUniqueId(targetResult, results = []) {
    return results.findIndex(({ uniqueId }) => uniqueId === targetResult.uniqueId);
}
async function logNextEvent(emitEvent, type, payload) {
    //@ts-expect-error will be fixed when relay updates
    await emitEvent(type, payload);
    return;
}
export const analyticsEventItemMetadata = (result, state) => {
    const identifier = documentIdentifier(result);
    const information = partialDocumentInformation(result, state);
    return {
        uniqueFieldName: identifier.contentIDKey,
        uniqueFieldValue: identifier.contentIDValue,
        title: information.documentTitle,
        author: information.documentAuthor,
        url: information.documentUri,
    };
};
