import { useCallback, useEffect, useRef, useState, } from "react";
import { useControllableState } from "../../../utils/hooks/index.js";
import { useDataTableContext } from "../root/DataTableRoot.context.js";
/**
 * TODO:
 * - Auto-width mode is hard now since that might cause layout-shifts on mount. But would be preferable to
 * be able to set "1fr" or similar and have it fill remaining space.
 */
function useTableColumnResize({ resizable = true, autoResizeOnce, resizeMin = 40, resizeMax = Infinity, value, defaultValue, onChange, thRef, colSpan, }) {
    const tableContext = useDataTableContext();
    const [isResizingWithKeyboard, setIsResizingWithKeyboard] = useState(false);
    const ignoreNextOnClick = useRef(false);
    const [width, setWidth] = useControllableState({
        value,
        defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : (colSpan !== null && colSpan !== void 0 ? colSpan : 1) * 140,
        /**
         * TODO:
         * - Potential optimization: Only call when width as "stopped" changing, e.g. on mouse up or after a debounce when resizing with keyboard.
         * Otherwise, this could cause excessive calls when resizing quickly.
         */
        onChange,
    });
    const setClampedWidth = useCallback((newWidth) => {
        setWidth(Math.min(Math.max(newWidth, resizeMin), resizeMax));
    }, [resizeMin, resizeMax, setWidth]);
    // biome-ignore lint/correctness/useExhaustiveDependencies: We only want to run this on mount and when autoResizeOnce changes
    useEffect(function autoResizeColumn() {
        if (!autoResizeOnce) {
            return;
        }
        const newColumnWidth = getAutoColumnWidth(thRef);
        if (newColumnWidth) {
            setClampedWidth(newColumnWidth);
        }
    }, [autoResizeOnce]);
    const handleOnClick = useCallback(() => {
        // We need to use the onClick event in order to support screen readers properly,
        // since some of them only send a mouse click (no kbd events) when pressing enter/space.
        // We detect a "screen reader click" by checking if we had a mouseUp event right before.
        if (ignoreNextOnClick.current) {
            ignoreNextOnClick.current = false;
            return;
        }
        setIsResizingWithKeyboard((prev) => !prev);
    }, []);
    const handleKeyDown = useCallback((event) => {
        var _a, _b;
        if (!isResizingWithKeyboard) {
            return;
        }
        const currentWidth = (_b = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) !== null && _b !== void 0 ? _b : 0;
        if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
            event.preventDefault();
            const delta = event.key === "ArrowRight" ? 20 : -20;
            setClampedWidth(currentWidth + delta);
            return;
        }
        if (event.key === "Home") {
            event.preventDefault();
            setClampedWidth(0); // will fall back to minWidth
            return;
        }
        if (event.key === "End") {
            event.preventDefault();
            const newWidth = getAutoColumnWidth(thRef);
            if (newWidth && newWidth > currentWidth) {
                setClampedWidth(newWidth);
            }
            return;
        }
        if (event.key === "Escape") {
            setIsResizingWithKeyboard(false);
        }
    }, [isResizingWithKeyboard, setClampedWidth, thRef]);
    const startResize = useCallback((startX) => {
        var _a, _b;
        const startWidth = (_b = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) !== null && _b !== void 0 ? _b : 0;
        function onPointerMove(clientX) {
            var _a, _b;
            const currentWidth = (_b = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) !== null && _b !== void 0 ? _b : 0;
            const newWidth = startWidth + (clientX - startX);
            if (newWidth > resizeMax) {
                setWidth(newWidth < currentWidth ? newWidth : currentWidth);
                return;
            }
            if (newWidth < resizeMin) {
                setWidth(newWidth > currentWidth ? newWidth : currentWidth);
                return;
            }
            setClampedWidth(newWidth);
        }
        function onMouseMove(e) {
            onPointerMove(e.clientX);
        }
        function onTouchMove(e) {
            e.preventDefault();
            onPointerMove(e.touches[0].clientX);
        }
        function cleanup() {
            document.removeEventListener("mousemove", onMouseMove);
            document.removeEventListener("touchmove", onTouchMove);
            setIsResizingWithKeyboard(false);
            // We only want onClick to trigger when using the keyboard
            // (we use onClick b.c. keyDown doesn't fire when using a screen reader)
            ignoreNextOnClick.current = true;
        }
        document.addEventListener("mousemove", onMouseMove);
        document.addEventListener("touchmove", onTouchMove, { passive: false });
        document.addEventListener("mouseup", cleanup, { once: true });
        document.addEventListener("touchend", cleanup, { once: true });
        document.addEventListener("touchcancel", cleanup, { once: true });
    }, [resizeMax, resizeMin, setWidth, setClampedWidth, thRef]);
    const handleMouseDown = useCallback((event) => {
        startResize(event.clientX);
    }, [startResize]);
    const handleTouchStart = useCallback((event) => {
        startResize(event.touches[0].clientX);
    }, [startResize]);
    // Auto-size column to fit content on double click
    const handleDoubleClick = useCallback(() => {
        const newColumnWidth = getAutoColumnWidth(thRef);
        if (newColumnWidth) {
            setClampedWidth(newColumnWidth);
        }
    }, [setClampedWidth, thRef]);
    if (tableContext.layout !== "fixed") {
        return {
            enabled: false,
        };
    }
    if (!resizable) {
        return {
            width,
            enabled: false,
        };
    }
    return {
        width,
        resizeHandlerProps: {
            onMouseDown: handleMouseDown,
            onTouchStart: handleTouchStart,
            onKeyDown: handleKeyDown,
            onBlur: () => setIsResizingWithKeyboard(false),
            onDoubleClick: handleDoubleClick,
            onClick: handleOnClick,
        },
        isResizingWithKeyboard,
        enabled: true,
    };
}
/**
 * Figures out how wide the column needs to be to fit all the content without truncation.
 */
function getAutoColumnWidth(thRef) {
    var _a;
    const th = thRef.current;
    const thContent = th.querySelector(".aksel-data-table__th-content");
    const thPaddingEl = th.querySelector(".aksel-data-table__cell-content");
    const rows = (_a = th.closest("table")) === null || _a === void 0 ? void 0 : _a.querySelectorAll("tbody tr, tfoot tr");
    if (!thContent || !thPaddingEl || !rows) {
        return;
    }
    // Find needed width for header cell
    const range = document.createRange();
    range.selectNodeContents(thContent);
    const contentWidth = range.getBoundingClientRect().width;
    const paddingElStyle = window.getComputedStyle(thPaddingEl);
    const thInlinePadding = parseInt(paddingElStyle.paddingLeft, 10) +
        parseInt(paddingElStyle.paddingRight, 10);
    let newColumnWidth = contentWidth + thInlinePadding;
    // Find column position
    let columnPosition = 1;
    let prevSibling = th.previousElementSibling;
    while (prevSibling) {
        columnPosition += prevSibling.colSpan;
        prevSibling = prevSibling.previousElementSibling;
    }
    // Find needed width for each cell in column in tbody and tfoot
    let skipRows = 0;
    for (const row of rows) {
        // Skip rows where the cell in this column is covered by a rowspan from a previous row
        if (skipRows > 0) {
            skipRows--;
            continue;
        }
        // Find cell
        let cell = row.firstChild;
        let currentPosition = cell.colSpan;
        while (columnPosition > currentPosition && cell.nextElementSibling) {
            cell = cell.nextElementSibling;
            currentPosition += cell.colSpan;
        }
        skipRows = cell.rowSpan - 1;
        // Find needed width
        const cellContent = cell.querySelector(".aksel-data-table__cell-content");
        if (!cellContent) {
            continue;
        }
        cellContent.style.width = "fit-content";
        const cellContentWidth = cellContent.scrollWidth;
        cellContent.style.removeProperty("width");
        let marginLeft = 0;
        if (cell.dataset.nested === "true") {
            const contentElStyle = window.getComputedStyle(cellContent);
            marginLeft = parseInt(contentElStyle.marginLeft, 10);
        }
        const widthNeededForThisCell = (cellContentWidth + marginLeft + 1) / cell.colSpan;
        if (widthNeededForThisCell > newColumnWidth) {
            newColumnWidth = Math.ceil(widthNeededForThisCell);
        }
    }
    // Make sure new width is not wider than the table container since that would be impractical
    const container = th.closest("div");
    const maxColWidth = ((container === null || container === void 0 ? void 0 : container.offsetWidth) || document.documentElement.clientWidth * 0.9) *
        0.95;
    return newColumnWidth > maxColWidth
        ? Math.floor(maxColWidth)
        : Math.ceil(newColumnWidth);
}
export { useTableColumnResize };
//# sourceMappingURL=useTableColumnResize.js.map