// (C) 2021-2026 GoodData Corporation
import { partition } from "lodash-es";
import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, dashboardFilterLocalIdentifier, dashboardMeasureValueFilterLocalIdentifier, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, isNoopAllTimeDashboardDateFilter, objRefToString, } from "@gooddata/sdk-model";
import { generateFilterLocalIdentifier } from "../../_infra/generators.js";
/**
 * Deeply merges partial working filter context into filter context definition.
 * @returns full working filter context.
 *
 * @internal
 */
export function applyFilterContext(filterContext, workingFilterContext, enableImmediateAttributeFilterDisplayAsLabelMigration) {
    const filters = filterContext.filters.map((appliedFilter) => {
        if (isDashboardAttributeFilter(appliedFilter)) {
            const workingFilter = workingFilterContext?.filters
                ?.filter(isDashboardAttributeFilter)
                .find((item) => item.attributeFilter?.localIdentifier ===
                appliedFilter.attributeFilter.localIdentifier);
            if (!workingFilter?.attributeFilter) {
                // Cross-type fallback: check if a text filter replaced this list filter
                // in working context (list→text migration).
                const crossTypeFilter = workingFilterContext?.filters?.find((item) => !isDashboardAttributeFilter(item) &&
                    isDashboardAttributeFilterItem(item) &&
                    dashboardAttributeFilterItemLocalIdentifier(item) ===
                        appliedFilter.attributeFilter.localIdentifier);
                if (crossTypeFilter) {
                    return crossTypeFilter;
                }
                return appliedFilter;
            }
            const displayForm = enableImmediateAttributeFilterDisplayAsLabelMigration
                ? appliedFilter.attributeFilter.displayForm
                : (workingFilter.attributeFilter.displayForm ?? appliedFilter.attributeFilter.displayForm);
            return {
                attributeFilter: {
                    ...appliedFilter.attributeFilter,
                    displayForm,
                    attributeElements: workingFilter.attributeFilter.attributeElements ??
                        appliedFilter.attributeFilter.attributeElements,
                    negativeSelection: workingFilter.attributeFilter.negativeSelection ??
                        appliedFilter.attributeFilter.negativeSelection,
                },
            };
        }
        else if (isDashboardAttributeFilterItem(appliedFilter)) {
            // Text filter types (arbitrary, match) — replace with working version if available
            const appliedLocalId = dashboardAttributeFilterItemLocalIdentifier(appliedFilter);
            const workingFilter = workingFilterContext?.filters?.find((item) => isDashboardAttributeFilterItem(item) &&
                dashboardAttributeFilterItemLocalIdentifier(item) === appliedLocalId);
            return (workingFilter ?? appliedFilter);
        }
        else if (isDashboardDateFilter(appliedFilter)) {
            const workingFilter = workingFilterContext?.filters
                ?.filter(isDashboardDateFilter)
                .find((item) => areObjRefsEqual(item.dateFilter.dataSet, appliedFilter.dateFilter.dataSet) ||
                (!item.dateFilter.dataSet && !appliedFilter.dateFilter.dataSet));
            if (!workingFilter?.dateFilter) {
                return appliedFilter;
            }
            const dateFilter = {
                dateFilter: {
                    ...appliedFilter.dateFilter,
                    type: workingFilter.dateFilter.type,
                    granularity: workingFilter.dateFilter.granularity,
                    from: workingFilter.dateFilter.from,
                    to: workingFilter.dateFilter.to,
                    boundedFilter: workingFilter.dateFilter.boundedFilter,
                    emptyValueHandling: workingFilter.dateFilter.emptyValueHandling,
                },
            };
            const { from, to } = dateFilter.dateFilter;
            if (from === undefined && to === undefined) {
                delete dateFilter.dateFilter.from;
                delete dateFilter.dateFilter.to;
            }
            return dateFilter;
        }
        else if (isDashboardMeasureValueFilter(appliedFilter)) {
            const workingFilter = workingFilterContext?.filters
                ?.filter(isDashboardMeasureValueFilter)
                .find((item) => dashboardFilterLocalIdentifier(item) ===
                appliedFilter.dashboardMeasureValueFilter.localIdentifier);
            if (!workingFilter) {
                return appliedFilter;
            }
            return {
                dashboardMeasureValueFilter: {
                    ...appliedFilter.dashboardMeasureValueFilter,
                    conditions: workingFilter.dashboardMeasureValueFilter.conditions,
                },
            };
        }
        else {
            throw new Error("Unknown filter type");
        }
    });
    const appliedCommonDateFilter = filterContext.filters.find(isDashboardCommonDateFilter);
    const workingCommonDateFilter = workingFilterContext?.filters.find(isDashboardCommonDateFilter);
    if (appliedCommonDateFilter ||
        !workingCommonDateFilter ||
        // Drop the working common date filter only when it is a true no-op All time filter.
        // An All time filter that carries `emptyValueHandling` (the "Empty values" preset) is a
        // meaningful change and must be kept, otherwise the dashboard would not detect it as changed.
        (isNoopAllTimeDashboardDateFilter(workingCommonDateFilter) && !appliedCommonDateFilter)) {
        return {
            ...filterContext,
            filters,
        };
    }
    return {
        ...filterContext,
        filters: [workingCommonDateFilter, ...filters],
    };
}
/**
 * Initializes filter context definition by ensuring filters have local identifiers
 * and are in proper order (common date filter first).
 *
 * @param filterContextDefinition - The filter context definition to initialize
 * @returns Initialized filter context definition with local identifiers and proper ordering
 * @internal
 */
export function initializeFilterContextDefinition(filterContextDefinition) {
    // Make sure all attribute filter types always have localId
    const filtersWithLocalId = filterContextDefinition.filters?.map((filter, i) => {
        if (isDashboardAttributeFilter(filter)) {
            return {
                attributeFilter: {
                    ...filter.attributeFilter,
                    localIdentifier: filter.attributeFilter.localIdentifier ??
                        generateFilterLocalIdentifier(filter.attributeFilter.displayForm, i),
                },
            };
        }
        if (isDashboardArbitraryAttributeFilter(filter)) {
            return {
                arbitraryAttributeFilter: {
                    ...filter.arbitraryAttributeFilter,
                    localIdentifier: filter.arbitraryAttributeFilter.localIdentifier ??
                        generateFilterLocalIdentifier(filter.arbitraryAttributeFilter.displayForm, i),
                },
            };
        }
        if (isDashboardMatchAttributeFilter(filter)) {
            return {
                matchAttributeFilter: {
                    ...filter.matchAttributeFilter,
                    localIdentifier: filter.matchAttributeFilter.localIdentifier ??
                        generateFilterLocalIdentifier(filter.matchAttributeFilter.displayForm, i),
                },
            };
        }
        return filter;
    });
    // Make sure that common date filter is always first if present
    // (when DateFilter is set to all time it's missing in filterContextDefinition and originalFilterContextDefinition)
    // We have to keep order of rest of array (attributeFilters and date filters with dimension)
    // it represents order of filters in filter bar
    const [commonDateFilter, otherFilters] = partition(filtersWithLocalId, isDashboardCommonDateFilter);
    const filters = [...commonDateFilter, ...otherFilters];
    return {
        ...filterContextDefinition,
        filters,
    };
}
/**
 * Creates a complete initialized FilterContextState from raw data.
 * Applies all initialization logic including local identifiers and filter ordering.
 *
 * @param filterContextDefinition - The filter context definition
 * @param originalFilterContextDefinition - Optional original definition for reset functionality
 * @param attributeFilterDisplayForms - Display forms for attribute filters
 * @param filterContextIdentity - Optional identity for persisted filter contexts
 * @returns Fully initialized FilterContextState
 * @internal
 */
export function initializeFilterContext(filterContextDefinition, originalFilterContextDefinition, attributeFilterDisplayForms, filterContextIdentity) {
    const initialized = initializeFilterContextDefinition(filterContextDefinition);
    return {
        filtersWithInvalidSelection: [],
        filterContextDefinition: initialized,
        workingFilterContextDefinition: { filters: [] },
        originalFilterContextDefinition,
        filterContextIdentity,
        attributeFilterDisplayForms: attributeFilterDisplayForms ?? [],
        defaultFilterOverrides: [],
    };
}
/**
 * @internal
 */
export function getFilterIdentifier(filter) {
    if (isDashboardAttributeFilter(filter)) {
        const localIdentifier = filter.attributeFilter.localIdentifier;
        if (!localIdentifier) {
            console.warn("Attribute filter without localIdentifier found. Using displayForm as fallback which may not be reliable.");
            return objRefToString(filter.attributeFilter.displayForm);
        }
        return localIdentifier;
    }
    if (isDashboardAttributeFilterItem(filter)) {
        const localIdentifier = dashboardAttributeFilterItemLocalIdentifier(filter);
        if (!localIdentifier) {
            throw new Error("Text attribute filter without localIdentifier found.");
        }
        return localIdentifier;
    }
    if (isDashboardDateFilter(filter)) {
        return ((filter.dateFilter.dataSet && objRefToString(filter.dateFilter.dataSet)) ?? "default_date_filter");
    }
    if (isDashboardMeasureValueFilter(filter)) {
        return dashboardMeasureValueFilterLocalIdentifier(filter);
    }
    throw new Error("Unknown filter type");
}
