// (C) 2021-2026 GoodData Corporation
import { compact, isEmpty, isEqual } from "lodash-es";
import { call, put, select } from "redux-saga/effects";
import { invariant } from "ts-invariant";
import { areObjRefsEqual, dashboardFilterLocalIdentifier, insightMeasures, isDashboardAttributeFilter, isDashboardMeasureValueFilter, isDateFilter, isSimpleMeasure, measureFilters, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
import { dashboardAttributeFilterItemToAttributeFilter, dashboardDateFilterToDateFilterByWidget, } from "../../../converters/filterConverters.js";
import { switchDashboardTab } from "../../commands/tabs.js";
import { drillToDashboardRequested, drillToDashboardResolved, } from "../../events/drill.js";
import { generateFilterLocalIdentifier } from "../../store/_infra/generators.js";
import { selectSupportsMultipleDateFilters } from "../../store/backendCapabilities/backendCapabilitiesSelectors.js";
import { selectCatalogDateAttributes } from "../../store/catalog/catalogSelectors.js";
import { selectEnableMultipleDateFilters } from "../../store/config/configSelectors.js";
import { selectInsightByRef } from "../../store/insights/insightsSelectors.js";
import { selectAttributeFilterConfigsDisplayAsLabelMap, selectAttributeFilterConfigsOverrides, } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
import { selectFilterContextAttributeFilterItems, selectFilterContextDateFilter, selectFilterContextDraggableFilterItems, } from "../../store/tabs/filterContext/filterContextSelectors.js";
import { selectAnalyticalWidgetByRef } from "../../store/tabs/layout/layoutSelectors.js";
import { convertIntersectionToAttributeFilters, removeIgnoredValuesFromDrillIntersection, } from "./common/intersectionUtils.js";
import { mergeDashboardAndSourceFilters } from "./common/mergeFilters.js";
import { getIncludedSourceFiltersForDashboard } from "./common/sourceDrillFilters.js";
export function* drillToDashboardHandler(ctx, cmd) {
    // put start event
    yield put(drillToDashboardRequested(ctx, cmd.payload.drillDefinition, cmd.payload.drillEvent, cmd.correlationId));
    // decide if we should use date filter (only if enabled and connected to a dataset)
    const widget = yield select(selectAnalyticalWidgetByRef(cmd.payload.drillEvent.widgetRef));
    const insight = yield select(selectInsightByRef(widget.insight));
    // if this bombs, widget is not an insight widget and something is seriously wrong
    invariant(insight);
    const ignoredDashboardFilterIds = new Set(cmd.payload.drillDefinition.ignoredDashboardFilters ?? []);
    const shouldUseDateFilter = !!widget.dateDataSet && !isDateFilterDisabled(insight);
    // widget's dateDataSet is not needed in common date filter because target dashboard widget's will use their own dateDataSet
    const commonDateFilter = shouldUseDateFilter
        ? removeIgnoredDashboardFilters([yield select(selectDrillingDateFilter)], ignoredDashboardFilterIds)[0]
        : undefined;
    // get proper attr filters
    const isDrillingToSelf = areObjRefsEqual(ctx.dashboardRef, cmd.payload.drillDefinition.target);
    const supportsMultipleDateFilters = yield select(selectSupportsMultipleDateFilters);
    const enableMultipleDateFilters = yield select(selectEnableMultipleDateFilters);
    const includeOtherDateFilters = supportsMultipleDateFilters && enableMultipleDateFilters;
    const allOtherFilters = yield select(selectAllOtherFilters);
    const allAttributeFilters = yield select(selectAllAttributeFilters);
    const widgetAwareFilters = isDrillingToSelf
        ? []
        : yield call(getWidgetAwareDashboardFilters, ctx, widget, includeOtherDateFilters);
    const candidateDashboardFilters = isDrillingToSelf
        ? // if drilling to self, just take all filters
            includeOtherDateFilters
                ? allOtherFilters
                : allAttributeFilters
        : // if drilling to other, resolve widget filter ignores
            widgetAwareFilters;
    const dashboardFilters = removeIgnoredDashboardFilters(candidateDashboardFilters, ignoredDashboardFilterIds);
    const dateAttributes = yield select(selectCatalogDateAttributes);
    const filteredIntersection = cmd.payload.drillDefinition.drillIntersectionIgnoredAttributes
        ? removeIgnoredValuesFromDrillIntersection(cmd.payload.drillEvent.drillContext.intersection ?? [], cmd.payload.drillDefinition.drillIntersectionIgnoredAttributes ?? [])
        : cmd.payload.drillEvent.drillContext.intersection;
    const filtersCount = dashboardFilters.length;
    const drillIntersectionFilters = convertIntersectionToAttributeFilters(filteredIntersection, dateAttributes.map((dA) => dA.attribute.ref), false, filtersCount);
    const [transformedFilters, attributeFilterConfigsFromTransformation] = transformToPrimaryLabelFilters(drillIntersectionFilters, filtersCount);
    const attributeFilterConfigs = attributeFilterConfigsFromTransformation;
    const intersectionFilters = transformedFilters;
    const attributeFilterDisplayAsLabelMap = yield select(selectAttributeFilterConfigsDisplayAsLabelMap);
    const dashboardFilterConfigs = getDashboardFilterConfigs(dashboardFilters, attributeFilterDisplayAsLabelMap);
    attributeFilterConfigs.push(...dashboardFilterConfigs);
    const includedSourceFilters = getIncludedSourceFiltersForDashboard(insight, cmd.payload.drillDefinition);
    // Merge dashboard and source filters by intersecting element values for matching attributes.
    // Unmatched filters from either side pass through as-is.
    const mergedFilters = mergeDashboardAndSourceFilters(dashboardFilters, includedSourceFilters);
    // Order matters: intersection > common date > merged dashboard+source.
    const resultingFilters = compact([...intersectionFilters, commonDateFilter, ...mergedFilters]);
    const targetTabLocalIdentifier = cmd.payload.drillDefinition.targetTabLocalIdentifier;
    if (targetTabLocalIdentifier && isDrillingToSelf) {
        yield put(switchDashboardTab(targetTabLocalIdentifier));
    }
    // put end event
    return drillToDashboardResolved(ctx, resultingFilters, attributeFilterConfigs, cmd.payload.drillDefinition, cmd.payload.drillEvent, cmd.correlationId);
}
function getDashboardFilterConfigs(dashboardFilters, attributeFilterDisplayAsLabelMap) {
    return dashboardFilters.reduce((result, filter) => {
        if (isDashboardAttributeFilter(filter)) {
            const localIdentifier = filter.attributeFilter.localIdentifier;
            const displayAsLabel = localIdentifier
                ? attributeFilterDisplayAsLabelMap.get(localIdentifier)
                : undefined;
            if (displayAsLabel && localIdentifier) {
                result.push({
                    localIdentifier,
                    displayAsLabel,
                });
            }
        }
        return result;
    }, []);
}
function transformToPrimaryLabelFilters(drillIntersectionFilters, filtersCount = 0) {
    const attributeFilterConfigs = [];
    const transformedFilters = drillIntersectionFilters.map((f, i) => {
        if (f.primaryLabel &&
            !areObjRefsEqual(f.primaryLabel, f.attributeFilter.attributeFilter.displayForm)) {
            const localIdentifier = f.attributeFilter.attributeFilter.localIdentifier ??
                generateFilterLocalIdentifier(f.attributeFilter.attributeFilter.displayForm, filtersCount + i);
            attributeFilterConfigs.push({
                localIdentifier,
                displayAsLabel: f.attributeFilter.attributeFilter.displayForm,
            });
            return {
                attributeFilter: {
                    ...f.attributeFilter.attributeFilter,
                    localIdentifier, // in case uuid was generated
                    displayForm: f.primaryLabel,
                },
            };
        }
        return f.attributeFilter;
    });
    return [transformedFilters, attributeFilterConfigs];
}
// result needs to be IDashboardDateFilter to allow optional dateDataSet
function selectDrillingDateFilter(state) {
    const globalDateFilter = selectFilterContextDateFilter(state);
    return globalDateFilter ?? newAllTimeDashboardDateFilter();
}
function selectAllAttributeFilters(state) {
    return selectFilterContextAttributeFilterItems(state);
}
function selectAllOtherFilters(state) {
    return selectFilterContextDraggableFilterItems(state);
}
function convertFilterItemsToFilters(filter, widget) {
    if ("dateFilter" in filter) {
        return dashboardDateFilterToDateFilterByWidget(filter, widget);
    }
    return dashboardAttributeFilterItemToAttributeFilter(filter);
}
function removeIgnoredDashboardFilters(filters, ignoredDashboardFilterIds) {
    if (!ignoredDashboardFilterIds.size) {
        return filters;
    }
    return filters.filter((filter) => {
        const localIdentifier = dashboardFilterLocalIdentifier(filter);
        return !localIdentifier || !ignoredDashboardFilterIds.has(localIdentifier);
    });
}
function* getWidgetAwareDashboardFilters(ctx, widget, includeOtherDateFilters) {
    const filtersIncludingDateFilters = yield select(selectFilterContextDraggableFilterItems);
    const attributeFilters = yield select(selectFilterContextAttributeFilterItems);
    const filterContextItems = includeOtherDateFilters ? filtersIncludingDateFilters : attributeFilters;
    const attributeFilterConfigs = yield select(selectAttributeFilterConfigsOverrides);
    // Drill-to-dashboard does not transfer measure value filters between dashboards in this iteration.
    // TODO INE: will be solved in https://gooddata.atlassian.net/browse/CQ-2285
    const drillTransferableItems = filterContextItems.filter((item) => !isDashboardMeasureValueFilter(item));
    const filtersPairs = drillTransferableItems.map((filter) => ({
        filter: convertFilterItemsToFilters(filter, widget),
        originalFilter: filter,
    }));
    const widgetFilters = yield call(getResolvedFiltersForWidget, ctx, widget, filtersPairs.map((pair) => pair.filter), attributeFilterConfigs);
    return filtersPairs
        .filter((pair) => widgetFilters.some((wf) => isEqual(wf, pair.filter)))
        .map((pair) => pair.originalFilter);
}
function isDateFilterDisabled(insight) {
    const measures = insightMeasures(insight);
    return isEmpty(measures)
        ? false
        : measures.every((measure) => {
            if (isSimpleMeasure(measure)) {
                const filters = measureFilters(measure);
                return !!filters?.some(isDateFilter);
            }
            return true;
        });
}
function getResolvedFiltersForWidget(ctx, widget, filters, attributeFilterConfigs) {
    return ctx.backend
        .workspace(ctx.workspace)
        .dashboards()
        .getResolvedFiltersForWidgetWithMultipleDateFilters(widget, [], filters, attributeFilterConfigs);
}
//# sourceMappingURL=drillToDashboardHandler.js.map