// (C) 2021-2026 GoodData Corporation
import { useCallback, useMemo, useState } from "react";
import { partition } from "lodash-es";
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, dashboardFilterObjRef, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMeasureValueFilter, } from "@gooddata/sdk-model";
import { addAttributeFilter as addAttributeFilterAction, addDateFilter as addDateFilterAction, addMeasureValueFilter as addMeasureValueFilterAction, } from "../../../model/commands/filters.js";
import { useDashboardDispatch, useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
import { dispatchAndWaitFor } from "../../../model/store/_infra/dispatchAndWaitFor.js";
import { selectAllCatalogDateDatasetsMap, selectCatalogAttributes, } from "../../../model/store/catalog/catalogSelectors.js";
import { getFilterIdentifier } from "../../../model/store/tabs/filterContext/filterContextUtils.js";
import { uiActions } from "../../../model/store/ui/index.js";
import { selectSelectedFilterIndex } from "../../../model/store/ui/uiSelectors.js";
/**
 * @internal
 */
export function isFilterBarFilterPlaceholder(object) {
    return object.type === "filterPlaceholder";
}
export function isFilterBarAttributeFilter(object) {
    return isDashboardAttributeFilterItem(object.filter);
}
/**
 * @internal
 */
export function isFilterBarDateFilterWithDimension(object) {
    if (!isFilterBarFilterPlaceholder(object) &&
        !isFilterBarFilterGroupItem(object) &&
        !isFilterBarMeasureValueFilter(object) &&
        isDashboardDateFilter(object.filter)) {
        return !!object.filter.dateFilter.dataSet;
    }
    return false;
}
/**
 * @internal
 */
export function isFilterBarMeasureValueFilter(object) {
    if (isFilterBarFilterPlaceholder(object) || isFilterBarFilterGroupItem(object)) {
        return false;
    }
    return isDashboardMeasureValueFilter(object.filter);
}
/**
 * @internal
 */
export function isFilterBarFilterGroupItem(object) {
    return object !== undefined && "groupConfig" in object;
}
function isNotDashboardCommonDateFilter(obj) {
    return (isDashboardAttributeFilterItem(obj) ||
        isDashboardDateFilterWithDimension(obj) ||
        isDashboardMeasureValueFilter(obj));
}
/**
 * @internal
 */
export function useFiltersWithAddedPlaceholder(filters, workingFilters) {
    const dispatch = useDashboardDispatch();
    const selectedFilterIndex = useDashboardSelector(selectSelectedFilterIndex);
    const allAttributes = useDashboardSelector(selectCatalogAttributes);
    const dateDatasetsMap = useDashboardSelector(selectAllCatalogDateDatasetsMap);
    const commonWorkingDateFilter = workingFilters?.find(isDashboardCommonDateFilter);
    const [draggableFilters, nonDraggableFilters] = partition(filters, isNotDashboardCommonDateFilter);
    // nonDraggableFilters may contain new filter types (arbitrary, match) alongside the common date filter.
    // Use a cast since TypeScript cannot narrow FilterContextItem exclusions through .find().
    const commonDateFilter = nonDraggableFilters.find((f) => isDashboardCommonDateFilter(f));
    const [dateFiltersWithDimensions, attributeFilters] = partition(draggableFilters, isDashboardDateFilterWithDimension);
    const [selectedDisplayForm, setSelectedDisplayForm] = useState();
    const [autoOpenFilter, setAutoOpenFilter] = useState();
    const addedAttributeFilter = useMemo(() => {
        if (selectedFilterIndex !== undefined) {
            if (selectedDisplayForm) {
                return {
                    ...{
                        type: "filterPlaceholder",
                        filterIndex: selectedFilterIndex,
                    },
                    selectedDisplayForm,
                };
            }
            return { type: "filterPlaceholder", filterIndex: selectedFilterIndex };
        }
        return undefined;
    }, [selectedFilterIndex, selectedDisplayForm]);
    const addDraggableFilterPlaceholder = useCallback(function (index) {
        dispatch(uiActions.selectFilterIndex(index));
    }, [dispatch]);
    const clearAddedFilter = useCallback(() => {
        setSelectedDisplayForm(undefined);
        dispatch(uiActions.clearFilterIndexSelection());
    }, [dispatch]);
    const closeAttributeSelection = useCallback(function () {
        // close after select attribute should not clear placeholder
        if (selectedDisplayForm) {
            return;
        }
        clearAddedFilter();
    }, [selectedDisplayForm, clearAddedFilter]);
    const draggableFiltersWithPlaceholder = useMemo(() => {
        const filterObjects = draggableFilters.map((filter, filterIndex) => {
            if (isDashboardAttributeFilterItem(filter)) {
                const workingFilter = workingFilters
                    ?.filter(isDashboardAttributeFilterItem)
                    .find((wf) => getFilterIdentifier(wf) === getFilterIdentifier(filter)) ?? filter;
                return {
                    filter,
                    filterIndex,
                    workingFilter,
                };
            }
            if (isDashboardMeasureValueFilter(filter)) {
                return {
                    filter,
                    filterIndex,
                };
            }
            const workingFilter = workingFilters
                ?.filter(isDashboardDateFilter)
                .find((wf) => getFilterIdentifier(wf) === getFilterIdentifier(filter)) ?? filter;
            return {
                filter,
                filterIndex,
                workingFilter,
            };
        });
        const containsAddedAttributeDisplayForm = selectedDisplayForm &&
            draggableFilters.some((draggableFilter) => {
                if (isDashboardAttributeFilterItem(draggableFilter)) {
                    return areObjRefsEqual(dashboardAttributeFilterItemDisplayForm(draggableFilter), selectedDisplayForm);
                }
                if (isDashboardMeasureValueFilter(draggableFilter)) {
                    return areObjRefsEqual(dashboardFilterObjRef(draggableFilter), selectedDisplayForm);
                }
                return areObjRefsEqual(draggableFilter.dateFilter.dataSet, selectedDisplayForm);
            });
        if (addedAttributeFilter === undefined || containsAddedAttributeDisplayForm) {
            return filterObjects;
        }
        filterObjects.splice(addedAttributeFilter.filterIndex, 0, addedAttributeFilter);
        return filterObjects;
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [addedAttributeFilter, draggableFilters, selectedDisplayForm]);
    // selects AF, DF with dimension, or MVF
    const selectDraggableFilter = useCallback(function (ref, selectionType) {
        if (!addedAttributeFilter) {
            return;
        }
        if (selectionType === "measure") {
            const usedMeasureValueFilter = draggableFilters
                .filter(isDashboardMeasureValueFilter)
                .find((filter) => areObjRefsEqual(dashboardFilterObjRef(filter), ref));
            if (usedMeasureValueFilter) {
                setAutoOpenFilter(dashboardFilterObjRef(usedMeasureValueFilter));
                clearAddedFilter();
            }
            else {
                setSelectedDisplayForm(ref);
                setAutoOpenFilter(ref);
                void dispatchAndWaitFor(dispatch, addMeasureValueFilterAction(ref, addedAttributeFilter.filterIndex)).finally(clearAddedFilter);
            }
            return;
        }
        // date filter added
        if (selectionType === "dateDataset") {
            const relatedDateDataset = dateDatasetsMap.get(ref);
            const usedDateDataset = dateFiltersWithDimensions.find((df) => areObjRefsEqual(df.dateFilter.dataSet, relatedDateDataset?.dataSet.ref));
            // We allowed just one dateFilter for one date dimension,
            if (usedDateDataset) {
                setAutoOpenFilter(usedDateDataset.dateFilter.dataSet);
                clearAddedFilter();
            }
            else {
                setSelectedDisplayForm(ref);
                setAutoOpenFilter(ref);
                void dispatchAndWaitFor(dispatch, addDateFilterAction(ref, addedAttributeFilter.filterIndex)).finally(clearAddedFilter);
            }
        }
        else {
            // attribute filter added
            const relatedAttribute = allAttributes.find((att) => att.displayForms.some((df) => areObjRefsEqual(df.ref, ref)));
            const usedDisplayForm = relatedAttribute?.displayForms.find((df) => {
                return attributeFilters
                    .filter(isDashboardAttributeFilterItem)
                    .find((x) => areObjRefsEqual(dashboardAttributeFilterItemDisplayForm(x), df.ref));
            });
            const primaryDisplayForm = relatedAttribute?.displayForms.find((df) => {
                return df.isPrimary;
            });
            // We allowed just one attributeFilter for one attribute,
            if (usedDisplayForm) {
                setAutoOpenFilter(usedDisplayForm.ref);
                clearAddedFilter();
            }
            else {
                setSelectedDisplayForm(ref);
                setAutoOpenFilter(primaryDisplayForm ? primaryDisplayForm.ref : ref);
                void dispatchAndWaitFor(dispatch, addAttributeFilterAction(ref, addedAttributeFilter.filterIndex, undefined, undefined, undefined, undefined, undefined, undefined, primaryDisplayForm?.ref)).finally(clearAddedFilter);
            }
        }
    }, [
        addedAttributeFilter,
        dateFiltersWithDimensions,
        attributeFilters,
        draggableFilters,
        dateDatasetsMap,
        allAttributes,
        clearAddedFilter,
        dispatch,
    ]);
    const onCloseAttributeFilter = useCallback(() => {
        setAutoOpenFilter(undefined);
    }, []);
    return [
        {
            commonDateFilter,
            commonWorkingDateFilter,
            draggableFiltersWithPlaceholder,
            draggableFiltersCount: draggableFilters.length,
            autoOpenFilter,
        },
        {
            addDraggableFilterPlaceholder,
            selectDraggableFilter,
            closeAttributeSelection,
            onCloseAttributeFilter,
        },
    ];
}
