import { createReducer } from '@reduxjs/toolkit';
import { isArray } from '../../utils/utils.js';
import { executeSearch, fetchMoreResults, fetchPage, } from '../search/search-actions.js';
import { loadCollection, registerFolding } from './folding-actions.js';
import { getFoldingInitialState, } from './folding-state.js';
import { getAllIncludedResultsFrom } from './folding-utils.js';
function getCollectionField(result, fields) {
    return result.raw[fields.collection];
}
function getParentField(result, fields) {
    return result.raw[fields.parent];
}
function getChildField(result, fields) {
    const rawValue = result.raw[fields.child];
    if (isArray(rawValue)) {
        return rawValue[0];
    }
    return rawValue;
}
function areDefinedAndEqual(value1, value2) {
    return (value1 || value2) !== undefined && value1 === value2;
}
function resolveChildrenFromFields(parent, results, fields, resolvedAncestors = []) {
    const sourceChildValue = getChildField(parent, fields);
    if (!sourceChildValue) {
        return [];
    }
    if (resolvedAncestors.indexOf(sourceChildValue) !== -1) {
        return [];
    }
    return results
        .filter((result) => {
        const isSameResultAsSource = getChildField(result, fields) === getChildField(parent, fields);
        const isChildOfSource = getParentField(result, fields) === sourceChildValue;
        return isChildOfSource && !isSameResultAsSource;
    })
        .map((result) => {
        const extendedResult = { ...result, searchUid: parent.searchUid };
        return {
            result: extendedResult,
            children: resolveChildrenFromFields(extendedResult, results, fields, [
                ...resolvedAncestors,
                sourceChildValue,
            ]),
        };
    });
}
function resolveRootFromFields(results, fields) {
    return results.find((result) => {
        const hasNoParent = getParentField(result, fields) === undefined;
        const isParentOfItself = areDefinedAndEqual(getParentField(result, fields), getChildField(result, fields));
        return hasNoParent || isParentOfItself;
    });
}
function resolveRootFromParentResult(result) {
    if (result.parentResult) {
        return resolveRootFromParentResult(result.parentResult);
    }
    return result;
}
function createCollectionFromResult(relevantResult, fields, searchUid, rootResult) {
    const resultsInCollection = getAllIncludedResultsFrom(relevantResult);
    const resultToUseAsRoot = rootResult ??
        resolveRootFromFields(resultsInCollection, fields) ??
        resolveRootFromParentResult(relevantResult);
    const extendedResultToUseAsRoot = { ...resultToUseAsRoot, searchUid };
    const children = resolveChildrenFromFields(extendedResultToUseAsRoot, resultsInCollection, fields);
    return {
        result: extendedResultToUseAsRoot,
        children,
        // To understand why "1" instead of "0", see here : https://coveord.atlassian.net/browse/SEARCHAPI-11075. totalNumberOfChildResults is off by 1 by the index design.
        moreResultsAvailable: relevantResult.totalNumberOfChildResults > children.length + 1,
        isLoadingMoreResults: false,
    };
}
function createCollections(results, fields, searchUid, rootResult) {
    const collections = {};
    results.forEach((result) => {
        const collectionId = getCollectionField(result, fields);
        if (!collectionId) {
            return;
        }
        if (!getChildField(result, fields) && !result.parentResult) {
            return;
        }
        collections[collectionId] = createCollectionFromResult(result, fields, searchUid, rootResult);
    });
    return collections;
}
function tryGetCollectionFromStateOrThrow(state, collectionId) {
    if (!state.collections[collectionId]) {
        throw new Error(`Missing collection ${collectionId} from ${Object.keys(state.collections)}: Folding most probably in an invalid state...`);
    }
    return state.collections[collectionId];
}
export const foldingReducer = createReducer(getFoldingInitialState(), (builder) => builder
    .addCase(executeSearch.fulfilled, (state, { payload }) => {
    state.collections = state.enabled
        ? createCollections(payload.response.results, state.fields, payload.response.searchUid)
        : {};
})
    .addCase(fetchPage.fulfilled, (state, { payload }) => {
    state.collections = state.enabled
        ? createCollections(payload.response.results, state.fields, payload.response.searchUid)
        : {};
})
    .addCase(fetchMoreResults.fulfilled, (state, { payload }) => {
    state.collections = state.enabled
        ? {
            ...state.collections,
            ...createCollections(payload.response.results, state.fields, payload.response.searchUid),
        }
        : {};
})
    .addCase(registerFolding, (state, { payload }) => state.enabled
    ? state
    : {
        enabled: true,
        collections: {},
        fields: {
            collection: payload.collectionField ?? state.fields.collection,
            parent: payload.parentField ?? state.fields.parent,
            child: payload.childField ?? state.fields.child,
        },
        filterFieldRange: payload.numberOfFoldedResults ?? state.filterFieldRange,
    })
    .addCase(loadCollection.pending, (state, { meta }) => {
    const collectionId = meta.arg;
    tryGetCollectionFromStateOrThrow(state, collectionId).isLoadingMoreResults = true;
})
    .addCase(loadCollection.rejected, (state, { meta }) => {
    const collectionId = meta.arg;
    tryGetCollectionFromStateOrThrow(state, collectionId).isLoadingMoreResults = false;
})
    .addCase(loadCollection.fulfilled, (state, { payload: { collectionId, results, rootResult, searchUid } }) => {
    const newCollections = createCollections(results, state.fields, searchUid, rootResult);
    if (!newCollections || !newCollections[collectionId]) {
        throw new Error(`Unable to create collection ${collectionId} from received results: ${JSON.stringify(results)}. Folding most probably in an invalid state... `);
    }
    state.collections[collectionId] = newCollections[collectionId];
    state.collections[collectionId].moreResultsAvailable = false;
}));
