import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import './Table.scss';
import { isFunction } from '../../assets/utils/functionUtils';
import classNames from 'classnames';
import { useEffect, useRef, useState, useCallback, useMemo, useImperativeHandle, forwardRef, } from 'react';
import { binarySearch, calculateFrozenWidth } from './utils/virtualization';
import { BUFFER_SIZE, DEFAULT_ROW_HEIGHT } from './utils/constants';
import TableHeader from './components/TableHeader';
import VirtualizedRows from './components/VirtualizedRows';
import { debounce } from '../../utils/debounce/debounce';
import { closestCorners, DndContext } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy, } from '@dnd-kit/sortable';
import { checkEmpty } from '../../utils/checkEmpty/checkEmpty';
const Table = forwardRef(({ data = [], columns = [], headerType, withCheckbox = false, onSelect, allSelected, partialSelected = false, withFixedHeader = true, borderWithRadius = false, headerCheckboxDisabled = false, noDataContent, height = '100%', className = '', tableHeadClass = '', tableBodyRowClass = '', headerTextColor, tableDataTextColor, headerIconName = '', headerIconOnClick = () => { }, draggable = false, tableRef = null, isRowCheckBoxDisable, isRowDisabled = true, tableHeaderZindex = 99, freezeColumns, headerIconTooltipTitle, onDragEnd, editMode = '', editComponent, accordionContent, getAccordionStatus = () => { }, selectedNode = { key: '', selected: '' }, uniqueRowIdForAccordion, }, ref) => {
    const containerRef = useRef(null);
    const totalRows = data.length;
    const setContainerRef = (node) => {
        containerRef.current = node;
        if (tableRef) {
            if (typeof tableRef === 'function') {
                tableRef(node);
            }
            else if (typeof tableRef === 'object' && 'current' in tableRef) {
                tableRef.current = node;
            }
        }
    };
    const [scrollTop, setScrollTop] = useState(0);
    const [containerHeight, setContainerHeight] = useState(0);
    // --- PERFORMANCE FIX 1: Use useRef for heights (No Re-renders) ---
    const rowHeightsMap = useRef({});
    // This version number triggers the re-calculation of the virtual list
    const [heightVersion, setHeightVersion] = useState(0);
    // Reset heights when data array reference changes
    useEffect(() => {
        setHeightVersion((version) => version + 1);
    }, [data]);
    // Container Resize Observer
    useEffect(() => {
        const container = containerRef.current;
        if (!container)
            return;
        setContainerHeight(container.clientHeight);
        const observer = new ResizeObserver((entries) => {
            for (let entry of entries) {
                // Use contentRect for the container
                const newHeight = entry.contentRect.height;
                if (Math.abs(newHeight - containerHeight) > 1) {
                    setContainerHeight(newHeight);
                }
            }
        });
        observer.observe(container);
        return () => {
            observer.unobserve(container);
            observer.disconnect();
        };
    }, [containerHeight]);
    const handleScroll = useCallback((e) => {
        // Use requestAnimationFrame for scroll updates if scrolling feels jittery,
        // generally direct state update is fine for React 18+ automatic batching.
        setScrollTop(e.currentTarget.scrollTop);
    }, []);
    // --- PERFORMANCE FIX 2: Batched Updates (Debounce) ---
    // If 100 rows mount, this ensures we only re-calculate layout ONCE after 20ms.
    const commitHeightUpdates = useMemo(() => debounce(() => {
        setHeightVersion((prev) => prev + 1);
    }, 20), []);
    const updateRowHeight = useCallback((index, height) => {
        const currentHeight = rowHeightsMap.current[index];
        // Only schedule update if height changed by > 1px
        if (currentHeight === undefined ||
            Math.abs(currentHeight - height) > 1) {
            // 1. Update the Ref (Synchronous, cheap)
            rowHeightsMap.current[index] = height;
            // 2. Trigger the Debounced Re-render (Asynchronous)
            commitHeightUpdates();
        }
    }, [commitHeightUpdates]);
    // --- PERFORMANCE FIX 3: Calculate Cumulative Heights from Ref ---
    const { cumulativeHeights, totalContentHeight } = useMemo(() => {
        let cumulative = 0;
        const heights = [0];
        for (let i = 0; i < totalRows; i++) {
            // FAST: Reading directly from the Ref object
            const height = rowHeightsMap.current[i] ?? DEFAULT_ROW_HEIGHT;
            cumulative += height;
            heights.push(cumulative);
        }
        return { cumulativeHeights: heights, totalContentHeight: cumulative };
    }, [totalRows, heightVersion]); // Only runs when 'heightVersion' updates
    // Virtualization Logic
    const visibleState = useMemo(() => {
        if (containerHeight === 0 || totalRows === 0) {
            return {
                startIndex: 0,
                endIndex: -1,
                paddingTop: 0,
                paddingBottom: 0,
                visibleRows: [],
            };
        }
        const startIdxPlusOne = binarySearch(cumulativeHeights, scrollTop + 1);
        let startIndex = startIdxPlusOne > 0 ? startIdxPlusOne - 1 : 0;
        const endIdxPlusOne = binarySearch(cumulativeHeights, scrollTop + containerHeight);
        let endIndex = endIdxPlusOne - 1;
        let bufferedStart = Math.max(0, startIndex - BUFFER_SIZE);
        let bufferedEnd = Math.min(totalRows - 1, endIndex + BUFFER_SIZE);
        const paddingTop = cumulativeHeights[bufferedStart];
        // Ensure we don't access out of bounds
        const nextHeight = cumulativeHeights[bufferedEnd + 1];
        const paddingBottom = totalContentHeight -
            (nextHeight !== undefined ? nextHeight : totalContentHeight);
        // Slicing data is cheap
        return {
            startIndex: bufferedStart,
            endIndex: bufferedEnd,
            paddingTop: paddingTop,
            paddingBottom: paddingBottom,
            visibleRows: data.slice(bufferedStart, bufferedEnd + 1),
        };
    }, [scrollTop, containerHeight, totalRows, cumulativeHeights, data]);
    const { startIndex, paddingTop, paddingBottom, visibleRows } = visibleState;
    let frozenWidth = 0;
    if (freezeColumns) {
        frozenWidth = calculateFrozenWidth(columns, freezeColumns);
    }
    const handleOnclick = useCallback((column, row) => {
        let { onClick, accessor } = column;
        if (onClick && isFunction(onClick)) {
            onClick(accessor, row);
        }
    }, []);
    const onSelectClick = useCallback((e, item) => {
        if (onSelect) {
            onSelect(e, item);
        }
    }, [onSelect]);
    // --- START OF NEW AUTOSCROLL LOGIC ---
    /**
     * Calculates the scroll position and imperatively scrolls the table to the row with the given ID.
     * @param rowId The ID or _id of the row to scroll to.
     */
    const scrollToRow = useCallback((rowId) => {
        // Assuming a safe, estimated fixed header height.
        // You MUST verify this value matches your actual TableHeader height.
        const FIXED_HEADER_HEIGHT = 50;
        const targetIndex = data.findIndex((row) => row.id === rowId || row._id === rowId);
        if (targetIndex === -1 ||
            !containerRef.current ||
            cumulativeHeights.length === 0) {
            return;
        }
        const container = containerRef.current;
        const targetRowTop = cumulativeHeights[targetIndex];
        const targetRowBottom = cumulativeHeights[targetIndex + 1];
        // --- 1. Calculate Initial Adjusted Position (Handling Fixed Header) ---
        // Scrolls the row down by the header height to bring it into full view.
        let adjustedScrollPosition = Math.max(0, targetRowTop - FIXED_HEADER_HEIGHT);
        const currentScrollTop = container.scrollTop;
        const currentScrollBottom = container.scrollTop + container.clientHeight;
        if (targetRowTop >= currentScrollTop + FIXED_HEADER_HEIGHT &&
            targetRowBottom <= currentScrollBottom) {
            return;
        }
        // --- 3. Edge Case: Preventing Overscrolling to the Bottom ---
        // Find the maximum possible scrollTop value (total height minus viewport height).
        const maxScrollTop = Math.max(0, totalContentHeight - container.clientHeight);
        // Clamp the adjusted position to ensure we don't scroll past the content end.
        adjustedScrollPosition = Math.min(adjustedScrollPosition, maxScrollTop);
        // --- 4. Perform the Scroll ---
        container.scrollTop = adjustedScrollPosition;
        setScrollTop(adjustedScrollPosition);
    }, [data, cumulativeHeights, totalContentHeight]);
    // 5. Expose the scroll function to the parent component via the ref
    useImperativeHandle(ref, () => ({
        // Expose the custom scroll function
        scrollToRow: scrollToRow,
        // You can expose other methods or the containerRef here if needed
    }));
    // --- END OF NEW AUTOSCROLL LOGIC ---
    const handleDragEnd = (event) => {
        const { active, over } = event;
        if (!over || active.id === over.id)
            return;
        const oldIndex = data.findIndex((item) => item._id === active.id || item.id === active.id);
        const newIndex = data.findIndex((item) => item._id === over.id || item.id === over.id);
        if (oldIndex === -1 || newIndex === -1)
            return;
        if (onDragEnd)
            onDragEnd(oldIndex, newIndex);
    };
    return (_jsx(DndContext, { collisionDetection: closestCorners, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { disabled: !draggable, items: data?.map((row) => row._id || row.id), strategy: verticalListSortingStrategy, children: _jsxs("div", { ref: setContainerRef, style: {
                    height,
                    overflow: 'auto',
                    position: 'relative',
                    scrollbarWidth: draggable ? 'none' : 'auto',
                    '--frozen-column-width': freezeColumns
                        ? `${frozenWidth}px`
                        : '0px',
                }, id: "ff-table-scroll-container", className: classNames('ff-table-scroll-container', {
                    'ff-table-fixed-header': withFixedHeader,
                    'ff-table-container--border-radius': borderWithRadius,
                }, className), onScroll: handleScroll, children: [_jsxs("table", { className: "ff-table-container", cellSpacing: 0, style: { height: totalContentHeight }, children: [_jsx(TableHeader, { columns: columns, freezeColumns: freezeColumns, headerType: headerType, tableHeadClass: tableHeadClass, headerTextColor: headerTextColor, headerCheckboxDisabled: headerCheckboxDisabled, withCheckbox: withCheckbox, allSelected: allSelected, partialSelected: partialSelected, headerIconName: headerIconName, headerIconOnClick: headerIconOnClick, headerIconTooltipTitle: headerIconTooltipTitle, tableHeaderZindex: tableHeaderZindex, onSelectClick: onSelectClick, withFixedHeader: withFixedHeader }), _jsx(VirtualizedRows, { paddingTop: paddingTop ?? 0, paddingBottom: paddingBottom, columnsLength: columns.length, visibleRows: visibleRows, startIndex: startIndex, columns: columns, tableBodyRowClass: tableBodyRowClass, handleOnclick: handleOnclick, tableDataTextColor: tableDataTextColor, withCheckbox: withCheckbox, onSelectClick: onSelectClick, isRowCheckBoxDisable: isRowCheckBoxDisable, isRowDisabled: isRowDisabled, freezeColumns: freezeColumns, frozenWidth: frozenWidth, updateRowHeight: updateRowHeight, draggable: draggable, editMode: editMode, editComponent: editComponent, getAccordionStatus: getAccordionStatus, accordionContent: accordionContent, selectedNode: selectedNode, uniqueRowIdForAccordion: uniqueRowIdForAccordion })] }), checkEmpty(data) && (_jsx("div", { className: "ff-no-data-content", style: { height: `calc(${height} - 50px)` }, children: noDataContent }))] }) }) }));
});
export default Table;
//# sourceMappingURL=Table.js.map