// (C) 2021-2026 GoodData Corporation
import { areObjRefsEqual, attributeLocalId, filterMeasureRef, filterObjRef, insightAttributes, insightFilters, insightMeasures, insightSetFilters, isDateFilter, isLocalIdRef, isMeasureValueFilter, isRankingFilter, measureLocalId, measureValueFilterDimensionality, } from "@gooddata/sdk-model";
import { addIntersectionFiltersToInsight } from "@gooddata/sdk-ui-ext";
import { removeIgnoredValuesFromDrillIntersection } from "./common/intersectionUtils.js";
import { getIncludedSourceInsightFilters, getIncludedSourceMeasureFilters, } from "./common/sourceDrillFilters.js";
/**
 * Applies drill intersection filters that are not excluded by `drillIntersectionIgnoredAttributes`,
 * and source insight filters/source measure filters that are explicitly included by
 * `includedSourceInsightFiltersObjRefs`/`includedSourceMeasureFiltersObjRefs`.
 * Dashboard filters are handled by drill dialog itself.
 *
 * If the target insight already contains filters:
 * - attribute filters are merged with existing target filters
 * - date filters are merged unless source and target date filters point to the same dataset
 * - for the same dataset, the source date filter replaces the target date filter
 * - target measure value/ranking filters are replaced when source includes measure value or ranking filter
 */
export function addDrillFiltersToInsight(targetInsight, sourceInsight, drillDefinition, drillEvent, supportsElementUris) {
    const intersection = drillEvent.drillContext.intersection;
    const filteredIntersection = drillDefinition.drillIntersectionIgnoredAttributes
        ? removeIgnoredValuesFromDrillIntersection(intersection ?? [], drillDefinition.drillIntersectionIgnoredAttributes ?? [])
        : intersection;
    const insightWithIntersectionFilters = addIntersectionFiltersToInsight(targetInsight, filteredIntersection, supportsElementUris);
    const insightWithSourceInsightFilters = addSourceInsightFiltersToInsight(insightWithIntersectionFilters, sourceInsight, drillDefinition);
    const insightWithSourceMeasureFilters = addSourceMeasureFiltersToInsight(insightWithSourceInsightFilters, sourceInsight, drillDefinition);
    return insightWithSourceMeasureFilters;
}
function addSourceInsightFiltersToInsight(targetInsight, sourceInsight, drillDefinition) {
    const sourceFiltersToApply = filterOutInvalidMeasureFilters(getIncludedSourceInsightFilters(sourceInsight, drillDefinition.includedSourceInsightFiltersObjRefs ?? []), targetInsight);
    const targetFilters = insightFilters(targetInsight);
    const mergedFilters = mergeInsightFilters(sourceFiltersToApply, targetFilters);
    return insightSetFilters(targetInsight, mergedFilters);
}
function addSourceMeasureFiltersToInsight(targetInsight, sourceInsight, drillDefinition) {
    if (!sourceInsight) {
        return targetInsight;
    }
    const sourceFiltersToApply = filterOutInvalidMeasureFilters(getIncludedSourceMeasureFilters(sourceInsight, drillDefinition), targetInsight);
    const targetFilters = insightFilters(targetInsight);
    const mergedFilters = mergeInsightFilters(sourceFiltersToApply, targetFilters);
    return insightSetFilters(targetInsight, mergedFilters);
}
/**
 * Filters out MVF/ranking filters that reference measures by localIdentifier not present
 * in the target insight, and strips MVF dimensionality refs that reference attributes
 * not present in the target insight. Such dangling localId references would cause the
 * execution normalizer to throw.
 */
function filterOutInvalidMeasureFilters(filters, targetInsight) {
    const targetMeasureLocalIds = new Set(insightMeasures(targetInsight).map(measureLocalId));
    const targetAttributeLocalIds = new Set(insightAttributes(targetInsight).map(attributeLocalId));
    return filters
        .filter((filter) => {
        if (!isMeasureValueFilter(filter) && !isRankingFilter(filter)) {
            return true;
        }
        const measureRef = filterMeasureRef(filter);
        if (!measureRef || !isLocalIdRef(measureRef)) {
            return true;
        }
        return targetMeasureLocalIds.has(measureRef.localIdentifier);
    })
        .map((filter) => {
        if (!isMeasureValueFilter(filter)) {
            return filter;
        }
        return stripInvalidDimensionality(filter, targetAttributeLocalIds);
    });
}
function stripInvalidDimensionality(filter, targetAttributeLocalIds) {
    const dimensionality = measureValueFilterDimensionality(filter);
    if (!dimensionality?.length) {
        return filter;
    }
    const validDimensionality = dimensionality.filter((ref) => !isLocalIdRef(ref) || targetAttributeLocalIds.has(ref.localIdentifier));
    if (validDimensionality.length === dimensionality.length) {
        return filter;
    }
    return {
        measureValueFilter: {
            ...filter.measureValueFilter,
            ...(validDimensionality.length > 0
                ? { dimensionality: validDimensionality }
                : { dimensionality: undefined }),
        },
    };
}
function mergeInsightFilters(sourceFiltersToApply, targetFilters) {
    const sourceContainsMeasureValueFilter = sourceFiltersToApply.some((filter) => isMeasureValueFilter(filter) || isRankingFilter(filter));
    const sourceDateFilters = sourceFiltersToApply.filter(isDateFilter);
    const sourceDateFilterRefs = sourceDateFilters.flatMap((sourceFilter) => {
        const sourceRef = filterObjRef(sourceFilter);
        return sourceRef ? [sourceRef] : [];
    });
    const targetFiltersWithoutConflicts = targetFilters.filter((targetFilter) => {
        if (sourceContainsMeasureValueFilter &&
            (isMeasureValueFilter(targetFilter) || isRankingFilter(targetFilter))) {
            return false;
        }
        if (!isDateFilter(targetFilter)) {
            return true;
        }
        const targetRef = filterObjRef(targetFilter);
        if (!targetRef) {
            return true;
        }
        return !sourceDateFilterRefs.some((sourceDateFilterRef) => areObjRefsEqual(sourceDateFilterRef, targetRef));
    });
    return [...targetFiltersWithoutConflicts, ...sourceFiltersToApply];
}
