import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// (C) 2007-2026 GoodData Corporation
import { useCallback, useMemo, useState } from "react";
import cx from "classnames";
import { has, keyBy, take } from "lodash-es";
import { FormattedMessage, injectIntl } from "react-intl";
import { Input } from "../Form/Input.js";
import { Message } from "../Messages/Message.js";
import { guidFor } from "./guid.js";
import { LegacyMultiSelectListWithIntl } from "./LegacyMultiSelectList.js";
import { LegacyMultiSelectListItem } from "./LegacyMultiSelectListItem.js";
function NoItemsFound() {
    return (_jsx("div", { className: "gd-list-noResults s-list-no-results", children: _jsx(FormattedMessage, { id: "gs.list.noItemsFound" }) }));
}
function LoadingMessage() {
    return _jsx("div", { children: "..." });
}
function LimitHitWarning({ limit, bounce }) {
    const classes = cx("gd-list-limitExceeded", {
        "animation-fadeIn": bounce,
    });
    return (_jsx(Message, { type: "warning", className: classes, children: _jsx(FormattedMessage, { id: "gs.list.limitExceeded", values: { limit } }) }));
}
/**
 * @internal
 * @deprecated This component is deprecated use InvertableList instead
 */
export function LegacyInvertableList({ className = undefined, filteredItemsCount, getItemKey = guidFor, height, isInverted = true, isLoading = false, isLoadingClass: IsLoadingClass = LoadingMessage, isMobile = false, itemHeight, items, itemsCount, limitHitWarningClass: LimitHitWarningClass = injectIntl(LimitHitWarning), listItemClass = LegacyMultiSelectListItem, maxSelectionSize, noItemsFound, noItemsFoundClass: NoItemsFoundClass = injectIntl(NoItemsFound), onRangeChange = () => { }, onSearch, onSelect = () => { }, searchPlaceholder = "", searchString = "", selection = [], showSearchField = true, smallSearch = false, tagName = "", width, actionsAsCheckboxes, selectAllCheckbox, rowItem, isSearchFieldAutoFocused = true, }) {
    const [notifyLimitHit, setNotifyLimitHit] = useState(false);
    const indexByKey = useCallback((items = []) => {
        return keyBy(items, getItemKey);
    }, [getItemKey]);
    const intersectItems = useCallback((items, otherItems) => {
        const otherItemsMap = indexByKey(otherItems);
        return items.filter((item) => {
            const itemKey = getItemKey(item);
            return !!otherItemsMap[itemKey];
        });
    }, [getItemKey, indexByKey]);
    const subtractItems = useCallback((items, otherItems) => {
        const otherItemsMap = indexByKey(otherItems);
        return items.filter((item) => {
            const itemKey = getItemKey(item);
            return !otherItemsMap[itemKey];
        });
    }, [getItemKey, indexByKey]);
    const toggleItemInSelection = useCallback((item) => {
        const selectionMap = indexByKey(selection);
        const itemKey = getItemKey(item);
        if (selectionMap[itemKey]) {
            delete selectionMap[itemKey];
        }
        else {
            selectionMap[itemKey] = item;
        }
        return Object.values(selectionMap);
    }, [getItemKey, indexByKey, selection]);
    /**
     * If change in selection happens to select all or unselect all items it is converted
     * to the respective empty selection.
     */
    const notifyUpstreamOfSelectionChange = useCallback((newSelection) => {
        let propsIsInverted = isInverted;
        let selectionToReturn;
        const lastItemSelected = !propsIsInverted && !searchString && newSelection.length === itemsCount;
        if (lastItemSelected) {
            selectionToReturn = [];
            propsIsInverted = !propsIsInverted;
        }
        else {
            selectionToReturn = newSelection;
        }
        onSelect(selectionToReturn, propsIsInverted);
    }, [isInverted, itemsCount, searchString, onSelect]);
    /**
     * Add unselected visible items to the selection until selection size limit is reached.
     */
    const growSelection = useCallback(() => {
        const selectionSizeLeft = maxSelectionSize - selection.length;
        const selectableItems = subtractItems(items, selection);
        const itemsToSelect = take(selectableItems, selectionSizeLeft);
        const newSelection = [...selection, ...itemsToSelect];
        notifyUpstreamOfSelectionChange(newSelection);
    }, [maxSelectionSize, items, selection, subtractItems, notifyUpstreamOfSelectionChange]);
    /**
     * Remove selected visible items from selection.
     */
    const shrinkSelection = useCallback(() => {
        const visibleSelection = intersectItems(items, selection);
        const newSelection = subtractItems(selection, visibleSelection);
        notifyUpstreamOfSelectionChange(newSelection);
    }, [items, intersectItems, selection, subtractItems, notifyUpstreamOfSelectionChange]);
    const onSelectHandler = useCallback((item) => {
        const newSelection = toggleItemInSelection(item);
        if (newSelection.length <= maxSelectionSize) {
            notifyUpstreamOfSelectionChange(newSelection);
        }
        if (newSelection.length >= maxSelectionSize) {
            // Flash the limit exceeded info
            setNotifyLimitHit(true);
            // remove the class that causes flashing animation
            setTimeout(() => {
                setNotifyLimitHit(false);
            }, 1000);
        }
    }, [toggleItemInSelection, maxSelectionSize, notifyUpstreamOfSelectionChange]);
    const onSelectAll = useCallback(() => {
        if (searchString) {
            if (isInverted) {
                shrinkSelection();
            }
            else {
                growSelection();
            }
        }
        else {
            onSelect([], true);
        }
    }, [searchString, isInverted, shrinkSelection, growSelection, onSelect]);
    const onSelectNone = useCallback(() => {
        if (searchString) {
            if (isInverted) {
                growSelection();
            }
            else {
                shrinkSelection();
            }
        }
        else {
            onSelect([], false);
        }
    }, [searchString, isInverted, growSelection, shrinkSelection, onSelect]);
    const onSelectOnly = useCallback((item) => {
        onSelect([item], false);
    }, [onSelect]);
    // private onSearchChange(searchString: string) {
    //     this.props.onSearch(searchString);
    // }
    const onRangeChangeHandler = useCallback((...args) => {
        onRangeChange(searchString, ...args);
    }, [onRangeChange, searchString]);
    const isItemChecked = useCallback((selectionMap, item) => {
        const key = getItemKey(item);
        const itemInSelection = has(selectionMap, key);
        // in inverted mode selection lists unchecked items
        // in normal mode selection contains checked items
        return isInverted ? !itemInSelection : itemInSelection;
    }, [getItemKey, isInverted]);
    const renderLimitHitWarning = useCallback(() => {
        const limitHit = selection.length >= maxSelectionSize;
        if (limitHit) {
            return _jsx(LimitHitWarningClass, { limit: maxSelectionSize, bounce: notifyLimitHit });
        }
        return null;
    }, [maxSelectionSize, selection.length, LimitHitWarningClass, notifyLimitHit]);
    const renderSearchField = useCallback(() => {
        return showSearchField ? (_jsx(Input, { autofocus: isSearchFieldAutoFocused, className: "gd-list-searchfield gd-flex-item-mobile s-attribute-filter-button-search-field", clearOnEsc: true, isSearch: true, isSmall: smallSearch, onChange: onSearch, placeholder: searchPlaceholder, value: searchString })) : null;
    }, [showSearchField, isSearchFieldAutoFocused, smallSearch, onSearch, searchPlaceholder, searchString]);
    const renderListOrNoItems = useCallback(() => {
        if (searchString && filteredItemsCount === 0) {
            return _jsx(NoItemsFoundClass, { height: height });
        }
        const selectionMap = indexByKey(selection);
        const isChecked = (item) => isItemChecked(selectionMap, item);
        const listProps = {
            className,
            filteredItemsCount,
            getItemKey,
            height,
            isInverted,
            isLoading,
            isLoadingClass: IsLoadingClass,
            isMobile,
            itemHeight,
            items,
            itemsCount: filteredItemsCount,
            limitHitWarningClass: LimitHitWarningClass,
            listItemClass,
            maxSelectionSize,
            noItemsFound,
            noItemsFoundClass: NoItemsFoundClass,
            onRangeChange,
            onSearch,
            onSelect,
            searchPlaceholder,
            searchString,
            selection,
            showSearchField,
            smallSearch,
            tagName,
            width,
            actionsAsCheckboxes,
            selectAllCheckbox,
            rowItem,
            isSearchFieldAutoFocused,
        };
        return (_jsx(LegacyMultiSelectListWithIntl, { ...listProps, onSelect: onSelectHandler, onSelectAll: onSelectAll, onSelectNone: onSelectNone, onSelectOnly: onSelectOnly, items: items, isSelected: isChecked, isSearching: !!searchString.length, listItemClass: listItemClass, onRangeChange: onRangeChangeHandler, tagName: tagName }));
    }, [
        searchString,
        filteredItemsCount,
        NoItemsFoundClass,
        height,
        indexByKey,
        selection,
        isItemChecked,
        className,
        getItemKey,
        isInverted,
        isLoading,
        IsLoadingClass,
        isMobile,
        itemHeight,
        items,
        LimitHitWarningClass,
        listItemClass,
        maxSelectionSize,
        noItemsFound,
        onRangeChange,
        onSearch,
        onSelect,
        searchPlaceholder,
        showSearchField,
        smallSearch,
        tagName,
        width,
        actionsAsCheckboxes,
        selectAllCheckbox,
        rowItem,
        isSearchFieldAutoFocused,
        onSelectHandler,
        onSelectAll,
        onSelectNone,
        onSelectOnly,
        onRangeChangeHandler,
    ]);
    const renderLoading = useCallback(() => {
        return _jsx(IsLoadingClass, { height: height });
    }, [IsLoadingClass, height]);
    const renderList = useCallback(() => {
        return isLoading ? (renderLoading()) : (_jsxs("div", { className: "gd-flex-item-stretch-mobile gd-flex-row-container-mobile", children: [renderListOrNoItems(), renderLimitHitWarning()] }));
    }, [isLoading, renderListOrNoItems, renderLimitHitWarning, renderLoading]);
    const classNames = useMemo(() => cx(className, {
        "gd-flex-item-stretch-mobile": isMobile,
        "gd-flex-row-container-mobile": isMobile,
    }), [className, isMobile]);
    return (_jsxs("div", { className: classNames, children: [renderSearchField(), renderList()] }));
}
