// (C) 2025-2026 GoodData Corporation
import { useCallback, useMemo, useRef, useState } from "react";
import { useIntl } from "react-intl";
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, isDashboardAttributeFilterItem, isDashboardDateFilter, } from "@gooddata/sdk-model";
import { useDashboardSelector } from "../../model/react/DashboardStoreProvider.js";
import { selectCatalogAttributes, selectCatalogDateDatasets, } from "../../model/store/catalog/catalogSelectors.js";
import { selectEnableNewScheduledExport } from "../../model/store/config/configSelectors.js";
import { selectAutomationCommonDateFilterId, selectDashboardLockedFilters, } from "../../model/store/filtering/dashboardFilterSelectors.js";
import { selectPersistedDashboardFilterContextDateFilterConfig } from "../../model/store/meta/metaSelectors.js";
import { selectAttributeFilterConfigsOverrides, selectAttributeFilterConfigsOverridesByTab, } from "../../model/store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
import { selectDateFilterConfigOverridesByTab } from "../../model/store/tabs/dateFilterConfig/dateFilterConfigSelectors.js";
import { selectDateFilterConfigsOverrides, selectDateFilterConfigsOverridesByTab, } from "../../model/store/tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
import { areFiltersMatchedByIdentifier, getCatalogAttributesByFilters, getCatalogDateDatasetsByFilters, getFilterByCatalogItemRef, getFilterLocalIdentifier, getFilterTitle, getNonHiddenFilters, getNonSelectedFilters, } from "./utils.js";
/**
 * Computes visible filters by removing hidden filters.
 */
function computeVisibleFilters(selectedFilters, context) {
    return getNonHiddenFilters(selectedFilters, context.attributeConfigs, context.dateConfigs, context.isCommonDateFilterHidden, context.disableDateFilters);
}
/**
 * Computes catalog attributes available for the Add filter dropdown.
 */
function computeAddDropdownAttributes(nonSelectedFilters, context) {
    return getCatalogAttributesByFilters(nonSelectedFilters, context.allAttributes, context.attributeConfigs);
}
/**
 * Computes catalog date datasets available for the Add filter dropdown.
 */
function computeAddDropdownDateDatasets(nonSelectedFilters, context) {
    return getCatalogDateDatasetsByFilters(nonSelectedFilters, context.allDateDatasets, context.dateConfigs);
}
/**
 * Logic for handling inner filters component logic.
 */
export const useAutomationFilters = ({ availableFilters, selectedFilters, onFiltersChange, onStoreFiltersChange, disableDateFilters = false, }) => {
    const intl = useIntl();
    const allAttributes = useDashboardSelector(selectCatalogAttributes);
    const allDateDatasets = useDashboardSelector(selectCatalogDateDatasets);
    const attributeConfigs = useDashboardSelector(selectAttributeFilterConfigsOverrides);
    const dateConfigs = useDashboardSelector(selectDateFilterConfigsOverrides);
    const dateFilterConfig = useDashboardSelector(selectPersistedDashboardFilterContextDateFilterConfig);
    const commonDateFilterId = useDashboardSelector(selectAutomationCommonDateFilterId);
    const lockedFilters = useDashboardSelector(selectDashboardLockedFilters);
    const enableNewScheduledExport = useDashboardSelector(selectEnableNewScheduledExport);
    const [filterAnnouncement, setFilterAnnouncement] = useState("");
    const addFilterButtonRef = useRef(null);
    const filterGroupRef = useRef(null);
    const isCommonDateFilterHidden = dateFilterConfig?.mode === "hidden";
    // Create processing context for helper functions
    const processingContext = useMemo(() => ({
        allAttributes,
        allDateDatasets,
        attributeConfigs,
        dateConfigs,
        isCommonDateFilterHidden,
        disableDateFilters,
    }), [
        allAttributes,
        allDateDatasets,
        attributeConfigs,
        dateConfigs,
        isCommonDateFilterHidden,
        disableDateFilters,
    ]);
    const visibleFilters = useMemo(() => computeVisibleFilters(selectedFilters, processingContext), [selectedFilters, processingContext]);
    const nonSelectedFilters = useMemo(() => getNonSelectedFilters(availableFilters, selectedFilters), [availableFilters, selectedFilters]);
    const attributes = useMemo(() => computeAddDropdownAttributes(nonSelectedFilters, processingContext), [nonSelectedFilters, processingContext]);
    const dateDatasets = useMemo(() => computeAddDropdownDateDatasets(nonSelectedFilters, processingContext), [nonSelectedFilters, processingContext]);
    const focusAddFilterButton = useCallback(() => {
        //focus add button, use requestAnimationFrame to wait for rerender
        requestAnimationFrame(() => {
            addFilterButtonRef.current?.focus();
        });
    }, []);
    const focusFilterGroup = useCallback(() => {
        requestAnimationFrame(() => {
            if (filterGroupRef.current) {
                // set tabindex to 0 to make the filter group focusable and preserve the tab order
                filterGroupRef.current.tabIndex = 0;
                filterGroupRef.current.focus();
            }
        });
    }, []);
    const announceFiltersChanged = useCallback((message) => {
        setTimeout(() => {
            // Defer announcement to next render so screen reader doesn't skip it
            setFilterAnnouncement(message);
        });
    }, []);
    const makeFilterGroupUnfocusable = useCallback(() => {
        requestAnimationFrame(() => {
            if (filterGroupRef.current) {
                filterGroupRef.current.removeAttribute("tabindex");
            }
        });
    }, []);
    const handleChangeFilter = useCallback((filter) => {
        if (!filter) {
            return;
        }
        const filterTitle = getFilterTitle(filter, allAttributes, allDateDatasets, intl);
        const message = intl.formatMessage({ id: "automationFilters.announcement.filterChanged" }, { title: filterTitle });
        announceFiltersChanged(message);
        const updatedFilters = selectedFilters.map((prevFilter) => {
            if (areFiltersMatchedByIdentifier(prevFilter, filter)) {
                return filter;
            }
            return prevFilter;
        });
        onFiltersChange(updatedFilters, enableNewScheduledExport);
    }, 
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [onFiltersChange, selectedFilters, allAttributes, allDateDatasets, intl]);
    const handleDeleteFilter = useCallback((filter) => {
        const filterTitle = getFilterTitle(filter, allAttributes, allDateDatasets, intl);
        const message = intl.formatMessage({ id: "automationFilters.announcement.filterRemoved" }, { title: filterTitle });
        announceFiltersChanged(message);
        const updatedFilters = selectedFilters.filter((prevFilter) => !areFiltersMatchedByIdentifier(prevFilter, filter));
        onFiltersChange(updatedFilters, enableNewScheduledExport);
        focusFilterGroup();
    }, 
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [onFiltersChange, focusFilterGroup, selectedFilters, allAttributes, allDateDatasets, intl]);
    const handleAddFilter = useCallback((catalogItemRef, attributes, dateDatasets) => {
        // We need to go through all display forms of the attribute in case
        // the filter is using different display form.
        const selectedAttributeDisplayForms = attributes
            .find((attribute) => attribute.displayForms.some((df) => areObjRefsEqual(df.ref, catalogItemRef)))
            ?.displayForms?.map((df) => df.ref) ?? [];
        const selectedDateDataSets = dateDatasets.filter((ds) => areObjRefsEqual(ds.dataSet.ref, catalogItemRef));
        const attributeFilter = selectedAttributeDisplayForms.reduce((acc, displayFormRef) => acc || getFilterByCatalogItemRef(displayFormRef, nonSelectedFilters), undefined);
        const dateFilter = selectedDateDataSets.reduce((acc, dateDataSet) => acc || getFilterByCatalogItemRef(dateDataSet.dataSet.ref, nonSelectedFilters), undefined);
        const filter = attributeFilter || dateFilter;
        if (filter) {
            const filterTitle = getFilterTitle(filter, allAttributes, allDateDatasets, intl);
            const message = intl.formatMessage({ id: "automationFilters.announcement.filterAdded" }, { title: filterTitle });
            const updatedFilters = [...selectedFilters, filter];
            onFiltersChange(updatedFilters, enableNewScheduledExport);
            announceFiltersChanged(message);
            setTimeout(focusAddFilterButton);
        }
    }, 
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [
        nonSelectedFilters,
        onFiltersChange,
        selectedFilters,
        allAttributes,
        allDateDatasets,
        intl,
        focusAddFilterButton,
    ]);
    const handleStoreFiltersChange = useCallback((value) => {
        onStoreFiltersChange(value, selectedFilters, undefined);
    }, [onStoreFiltersChange, selectedFilters]);
    // Function to set ref for AttributesDropdown and also add filter button ref for custom focus management
    const setAddFilterButtonRefs = useCallback((element, dropdownButtonRef) => {
        addFilterButtonRef.current = element;
        if (dropdownButtonRef && element) {
            dropdownButtonRef.current = element;
        }
    }, []);
    return {
        commonDateFilterId,
        lockedFilters,
        visibleFilters,
        attributes,
        dateDatasets,
        attributeConfigs,
        dateConfigs,
        filterAnnouncement,
        filterGroupRef,
        makeFilterGroupUnfocusable,
        handleChangeFilter,
        handleDeleteFilter,
        handleAddFilter,
        handleStoreFiltersChange,
        setAddFilterButtonRefs,
    };
};
/**
 * Hook for processing filters structured per tab.
 * Applies the same business logic as useAutomationFilters to each tab's filters.
 * Returns processed filters and handlers for add/change/delete operations per tab.
 */
export const useAutomationFiltersByTab = ({ filtersByTab, editedFiltersByTab, onFiltersByTabChange, onStoreFiltersChange, disableDateFilters = false, }) => {
    const allAttributes = useDashboardSelector(selectCatalogAttributes);
    const allDateDatasets = useDashboardSelector(selectCatalogDateDatasets);
    const commonDateFilterId = useDashboardSelector(selectAutomationCommonDateFilterId);
    const [filterAnnouncement] = useState("");
    const addFilterButtonRef = useRef(null);
    const filterGroupRef = useRef(null);
    // Get per-tab filter configs
    const attributeConfigsByTab = useDashboardSelector(selectAttributeFilterConfigsOverridesByTab);
    const dateConfigsByTab = useDashboardSelector(selectDateFilterConfigsOverridesByTab);
    const dateFilterConfigByTab = useDashboardSelector(selectDateFilterConfigOverridesByTab);
    const processedFiltersByTab = useMemo(() => {
        if (!filtersByTab || filtersByTab.length === 0) {
            return undefined;
        }
        return filtersByTab.map((tab) => {
            const tabId = tab.tabId;
            // Get configs specific to this tab
            const attributeConfigs = attributeConfigsByTab[tabId] ?? [];
            const dateConfigs = dateConfigsByTab[tabId] ?? [];
            const dateFilterConfig = dateFilterConfigByTab[tabId];
            const isCommonDateFilterHidden = dateFilterConfig?.mode === "hidden";
            // Create processing context for this specific tab
            const processingContext = {
                allAttributes,
                allDateDatasets,
                attributeConfigs,
                dateConfigs,
                isCommonDateFilterHidden,
                disableDateFilters,
            };
            // Use edited filters if available, otherwise default selected filters
            const selectedFilters = editedFiltersByTab?.[tabId] ?? tab.defaultSelectedFilters;
            const availableFilters = tab.availableFilters;
            // Apply visible filter logic (removes hidden filters based on config)
            const visibleFilters = computeVisibleFilters(selectedFilters, processingContext);
            // Compute non-selected filters for Add dropdown
            const nonSelectedFilters = getNonSelectedFilters(availableFilters, selectedFilters);
            // Compute catalog items for Add dropdown
            const attributes = computeAddDropdownAttributes(nonSelectedFilters, processingContext);
            const dateDatasets = computeAddDropdownDateDatasets(nonSelectedFilters, processingContext);
            return {
                tabId: tab.tabId,
                tabTitle: tab.tabTitle,
                visibleFilters,
                lockedFilters: tab.lockedFilters,
                attributes,
                dateDatasets,
                nonSelectedFilters,
                attributeConfigs,
                dateConfigs,
            };
        });
    }, [
        filtersByTab,
        editedFiltersByTab,
        allAttributes,
        allDateDatasets,
        attributeConfigsByTab,
        dateConfigsByTab,
        dateFilterConfigByTab,
        disableDateFilters,
    ]);
    // Handlers for per-tab filter operations (similar to original hook)
    const handleTabFilterChange = useCallback((tabId, updatedFilter) => {
        if (!editedFiltersByTab || !onFiltersByTabChange) {
            return;
        }
        const currentTabFilters = editedFiltersByTab[tabId] ?? [];
        if (!updatedFilter) {
            return;
        }
        const filterLocalId = getFilterLocalIdentifier(updatedFilter);
        const updatedTabFilters = currentTabFilters.map((f) => {
            const currentFilterLocalId = getFilterLocalIdentifier(f);
            return currentFilterLocalId === filterLocalId ? updatedFilter : f;
        });
        onFiltersByTabChange({
            ...editedFiltersByTab,
            [tabId]: updatedTabFilters,
        });
    }, [editedFiltersByTab, onFiltersByTabChange]);
    const handleTabFilterDelete = useCallback((tabId, filterToDelete) => {
        if (!editedFiltersByTab || !onFiltersByTabChange) {
            return;
        }
        const currentTabFilters = editedFiltersByTab[tabId] ?? [];
        const filterLocalId = getFilterLocalIdentifier(filterToDelete);
        const updatedTabFilters = currentTabFilters.filter((f) => {
            const currentFilterLocalId = getFilterLocalIdentifier(f);
            return currentFilterLocalId !== filterLocalId;
        });
        onFiltersByTabChange({
            ...editedFiltersByTab,
            [tabId]: updatedTabFilters,
        });
    }, [editedFiltersByTab, onFiltersByTabChange]);
    const handleTabFilterAdd = useCallback((tabId, displayForm, attributes, dateDatasets) => {
        if (!editedFiltersByTab || !onFiltersByTabChange || !filtersByTab) {
            return;
        }
        const currentTabFilters = editedFiltersByTab[tabId] ?? [];
        const tabData = filtersByTab.find((t) => t.tabId === tabId);
        if (!tabData) {
            return;
        }
        // We need to go through all display forms of the attribute in case
        // the filter is using different display form (same logic as flat version).
        const selectedAttributeDisplayForms = attributes
            .find((attribute) => attribute.displayForms.some((df) => areObjRefsEqual(df.ref, displayForm)))
            ?.displayForms?.map((df) => df.ref) ?? [];
        const selectedDateDataSets = dateDatasets.filter((ds) => areObjRefsEqual(ds.dataSet.ref, displayForm));
        // Search through all display forms to find matching filter
        const availableFilter = tabData.availableFilters.find((f) => {
            if (isDashboardAttributeFilterItem(f)) {
                return selectedAttributeDisplayForms.some((dfRef) => areObjRefsEqual(dashboardAttributeFilterItemDisplayForm(f), dfRef));
            }
            else if (isDashboardDateFilter(f)) {
                return selectedDateDataSets.some((ds) => areObjRefsEqual(f.dateFilter.dataSet, ds.dataSet.ref));
            }
            return false;
        });
        if (availableFilter) {
            onFiltersByTabChange({
                ...editedFiltersByTab,
                [tabId]: [...currentTabFilters, availableFilter],
            });
        }
    }, [editedFiltersByTab, onFiltersByTabChange, filtersByTab]);
    const handleStoreFiltersChange = useCallback((value) => {
        onStoreFiltersChange(value, undefined, editedFiltersByTab);
    }, [editedFiltersByTab, onStoreFiltersChange]);
    const makeFilterGroupUnfocusable = useCallback(() => {
        requestAnimationFrame(() => {
            if (filterGroupRef.current) {
                filterGroupRef.current.removeAttribute("tabindex");
            }
        });
    }, []);
    const setAddFilterButtonRefs = useCallback((element, dropdownButtonRef) => {
        addFilterButtonRef.current = element;
        if (dropdownButtonRef && element) {
            dropdownButtonRef.current = element;
        }
    }, []);
    // Get common configs from first tab
    const firstTab = filtersByTab?.[0];
    const activeTabId = firstTab?.tabId;
    const attributeConfigs = attributeConfigsByTab[activeTabId ?? ""] ?? [];
    const dateConfigs = dateConfigsByTab[activeTabId ?? ""] ?? [];
    // Compute all locked filters across all tabs
    const lockedFilters = useMemo(() => {
        if (!processedFiltersByTab) {
            return [];
        }
        return processedFiltersByTab.flatMap((tab) => tab.lockedFilters);
    }, [processedFiltersByTab]);
    return {
        commonDateFilterId,
        lockedFilters,
        processedFiltersByTab,
        attributeConfigs,
        dateConfigs,
        filterAnnouncement,
        filterGroupRef,
        handleTabFilterChange,
        handleTabFilterDelete,
        handleTabFilterAdd,
        handleStoreFiltersChange,
        makeFilterGroupUnfocusable,
        setAddFilterButtonRefs,
    };
};
//# sourceMappingURL=useAutomationFilters.js.map