// (C) 2021-2026 GoodData Corporation
import { uniqWith } from "lodash-es";
import { batchActions } from "redux-batched-actions";
import { call, put, select } from "redux-saga/effects";
import { areObjRefsEqual, isDashboardAttributeFilter, } from "@gooddata/sdk-model";
import { dashboardWasReset } from "../../events/dashboard.js";
import { selectAllCatalogDisplayFormsMap, selectCatalogParameters, } from "../../store/catalog/catalogSelectors.js";
import { selectDateFilterConfig, selectEnableImmediateAttributeFilterDisplayAsLabelMigration, selectSettings, } from "../../store/config/configSelectors.js";
import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/drillSelectors.js";
import { selectFilterViews } from "../../store/filterViews/filterViewsReducersSelectors.js";
import { selectPersistedDashboard } from "../../store/meta/metaSelectors.js";
import { selectActiveTabLocalIdentifier, selectTabs } from "../../store/tabs/tabsSelectors.js";
import { DEFAULT_TAB_ID } from "../../store/tabs/tabsState.js";
import { resolveInsights } from "../../utils/insightResolver.js";
import { applyDefaultFilterView } from "./common/filterViews.js";
import { insightReferencesFromDashboard } from "./common/insightReferences.js";
import { getMigratedAttributeFilters } from "./common/migratedAttributeFilters.js";
import { actionsToInitializeExistingDashboard, actionsToInitializeNewDashboard, } from "./common/stateInitializers.js";
export function* resetDashboardHandler(ctx, cmd) {
    const data = yield call(resetDashboardFromPersisted, ctx);
    yield put(batchActions(data.batch, "@@GDC.DASH/RESET"));
    return dashboardWasReset(ctx, data.persistedDashboard, cmd.correlationId);
}
export function* resetDashboardRuntime(ctx, cmd) {
    const data = yield call(resetDashboardFromPersisted, ctx);
    return {
        batch: batchActions(data.batch, "@@GDC.DASH/RESET"),
        reset: dashboardWasReset(ctx, data.persistedDashboard, cmd.correlationId),
    };
}
function* getFilterRelatedParamsPerTab(persistedTab, currentTab, isImmediateAttributeFilterMigrationEnabled) {
    const crossFilteringFiltersLocalIdentifiers = yield select(selectCrossFilteringFiltersLocalIdentifiers);
    const persistedTabFilters = persistedTab.filterContext?.filters?.filter(isDashboardAttributeFilter) ?? [];
    const currentTabFilters = currentTab?.filterContext?.filterContextDefinition?.filters?.filter(isDashboardAttributeFilter) ?? [];
    // Get ad-hoc attribute filter configs for this specific tab from currentTab state
    const adHocAttributeFilterConfigs = currentTab?.attributeFilterConfigs?.attributeFilterConfigs ?? [];
    // Get original filter context definition for this specific tab from currentTab state
    const originalFilterContext = currentTab?.filterContext?.originalFilterContextDefinition;
    const tabMigratedAttributeFilters = isImmediateAttributeFilterMigrationEnabled
        ? getMigratedAttributeFilters(persistedTabFilters, currentTabFilters, crossFilteringFiltersLocalIdentifiers)
        : [];
    const tabEffectiveAttributeFilterConfigs = isImmediateAttributeFilterMigrationEnabled
        ? mergeDashboardAttributeFilterConfigs(persistedTab.attributeFilterConfigs ?? [], adHocAttributeFilterConfigs, tabMigratedAttributeFilters)
        : (persistedTab.attributeFilterConfigs ?? []);
    const hasUnsavedFilterChanges = tabMigratedAttributeFilters.length > 0;
    const tabsEffectiveOriginalFilterContext = isImmediateAttributeFilterMigrationEnabled && hasUnsavedFilterChanges
        ? originalFilterContext
        : undefined;
    // Get dateFilterConfig for this tab
    // Use the current tab's effectiveDateFilterConfig if available, otherwise get it from settings
    const effectiveDateFilterConfig = currentTab?.dateFilterConfig?.effectiveDateFilterConfig;
    // Fallback: get from settings if not available in tab state
    const fallbackDateFilterConfig = yield select(selectDateFilterConfig);
    return {
        migratedAttributeFilters: tabMigratedAttributeFilters,
        effectiveAttributeFilterConfigs: tabEffectiveAttributeFilterConfigs,
        effectiveOriginalFilterContext: tabsEffectiveOriginalFilterContext,
        dateFilterConfig: effectiveDateFilterConfig || fallbackDateFilterConfig,
        dateFilterConfigSource: currentTab?.dateFilterConfig?.isUsingDashboardOverrides
            ? "dashboard"
            : "workspace",
    };
}
function* resetDashboardFromPersisted(ctx) {
    const persistedDashboard = yield select(selectPersistedDashboard);
    let batch = [];
    if (persistedDashboard) {
        /*
         * For dashboard that is already persisted the insights and effective date filter config can be used
         * as is (date filter config is read-only).
         *
         * The only exception is the insights: thanks to the Reload button in plugins, the dashboard could have been
         * reloaded with a different set of insights, so when resetting, we need to make sure that we still have all
         * the insights needed for the original dashboard shape.
         *
         * The call to create actions to initialize existing dashboard will use all this to set state
         * of filter context, layout and meta based on the contents of persisted dashboard; this is the
         * same logic as what is done during the initialization of the dashboard based on data from backend.
         *
         * Everything else can stay untouched.
         */
        const isImmediateAttributeFilterMigrationEnabled = yield select(selectEnableImmediateAttributeFilterDisplayAsLabelMigration);
        const currentTabs = yield select(selectTabs);
        const tabsToProcess = persistedDashboard.tabs && persistedDashboard.tabs.length > 0
            ? persistedDashboard.tabs
            : [
                {
                    localIdentifier: DEFAULT_TAB_ID,
                    title: "",
                    filterContext: persistedDashboard.filterContext,
                    attributeFilterConfigs: persistedDashboard.attributeFilterConfigs,
                    layout: persistedDashboard.layout,
                },
            ];
        // Compute migratedAttributeFilters, effectiveAttributeFilterConfigs, and dateFilterConfig for each tab
        const effectiveOriginalFilterContext = {};
        const migratedAttributeFilters = {};
        const effectiveAttributeFilterConfigs = {};
        const dateFilterConfig = {};
        const tabsDateFilterConfigSource = {};
        for (const tab of tabsToProcess) {
            const tabId = tab.localIdentifier;
            const currentTab = currentTabs?.find((t) => t.localIdentifier === tabId);
            const filterParams = yield call(getFilterRelatedParamsPerTab, tab, currentTab, isImmediateAttributeFilterMigrationEnabled);
            migratedAttributeFilters[tabId] = filterParams.migratedAttributeFilters;
            effectiveAttributeFilterConfigs[tabId] = filterParams.effectiveAttributeFilterConfigs;
            effectiveOriginalFilterContext[tabId] = filterParams.effectiveOriginalFilterContext;
            dateFilterConfig[tabId] = filterParams.dateFilterConfig;
            tabsDateFilterConfigSource[tabId] = filterParams.dateFilterConfigSource;
        }
        // end of ad-hoc migration content
        const settings = yield select(selectSettings);
        const filterViews = yield select(selectFilterViews);
        const dashboardWithUpdatedFilterContext = applyDefaultFilterView(persistedDashboard, filterViews);
        const insightRefsFromWidgets = insightReferencesFromDashboard(dashboardWithUpdatedFilterContext);
        const uniqueInsightRefsFromWidgets = uniqWith(insightRefsFromWidgets, areObjRefsEqual);
        const resolvedInsights = yield call(resolveInsights, ctx, uniqueInsightRefsFromWidgets);
        const displayForms = yield select(selectAllCatalogDisplayFormsMap);
        const resolvedInsightsValues = [...resolvedInsights.resolved.values()];
        const currentActiveTabId = yield select(selectActiveTabLocalIdentifier);
        // Validate that the current active tab exists in the persisted dashboard
        // If not (e.g., user created a new tab but didn't save), fall back to first tab
        const activeTabExistsInPersisted = persistedDashboard.tabs?.some((tab) => tab.localIdentifier === currentActiveTabId);
        const effectiveActiveTabId = activeTabExistsInPersisted
            ? currentActiveTabId
            : persistedDashboard.tabs?.[0]?.localIdentifier;
        const workspaceParameters = yield select(selectCatalogParameters);
        batch = (yield call(actionsToInitializeExistingDashboard, ctx, dashboardWithUpdatedFilterContext, resolvedInsightsValues, settings, isImmediateAttributeFilterMigrationEnabled, effectiveOriginalFilterContext, migratedAttributeFilters, effectiveAttributeFilterConfigs, dateFilterConfig, tabsDateFilterConfigSource, displayForms, persistedDashboard, effectiveActiveTabId, workspaceParameters));
    }
    else {
        const settings = yield select(selectSettings);
        /*
         * For dashboard that is not persisted, the dashboard component is reset to an 'empty' state.
         */
        const dateFilterConfig = yield select(selectDateFilterConfig);
        const displayForms = yield select(selectAllCatalogDisplayFormsMap);
        const { initActions } = yield call(actionsToInitializeNewDashboard, ctx, settings, dateFilterConfig, displayForms);
        batch = initActions;
    }
    return {
        batch,
        persistedDashboard,
    };
}
const mergeDashboardAttributeFilterConfigs = (originalConfigs = [], overridingConfigs = [], migratedAttributeFilters) => {
    const sanitizedOverridingConfigs = overridingConfigs.filter((config) => migratedAttributeFilters.some((filter) => filter.attributeFilter.localIdentifier === config.localIdentifier));
    const overriddenConfigs = originalConfigs.map((originalConfig) => {
        const overridingConfig = sanitizedOverridingConfigs.find((config) => config.localIdentifier === originalConfig.localIdentifier);
        if (!overridingConfig) {
            return originalConfig;
        }
        return {
            ...originalConfig,
            ...overridingConfig,
        };
    });
    const additionalOverridingConfigs = sanitizedOverridingConfigs.filter((config) => !overriddenConfigs.some((mergedConfig) => mergedConfig.localIdentifier === config.localIdentifier));
    return [...overriddenConfigs, ...additionalOverridingConfigs];
};
