"""JavaScript generator for overflow-based automatic visible row count adjustment."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/js/auto_fit.ipynb.

# %% auto #0
__all__ = ['generate_auto_fit_js', 'auto_fit_callback_name']

# %% ../../nbs/js/auto_fit.ipynb #8f683fda
from ..core.html_ids import VirtualCollectionHtmlIds
from cjm_fasthtml_virtual_collection.core.models import (
    VirtualCollectionConfig, VirtualCollectionUrls,
)

# %% ../../nbs/js/auto_fit.ipynb #d5ac25fe
def generate_auto_fit_js(
    ids: VirtualCollectionHtmlIds,       # HTML IDs for this collection
    config: VirtualCollectionConfig,      # Collection config
    urls: VirtualCollectionUrls,          # URL bundle (for update_viewport)
    total_items: int = 0,                 # Initial total item count (fallback)
    initial_visible: int = 1,             # Initial visible row count
) -> str:  # JavaScript code fragment
    """Generate JS for overflow-based auto-fit of visible row count.

    Measures actual table overflow against wrapper height. Grows incrementally
    with opacity:0 validation, shrinks via batch estimation. Adapted from the
    cjm-fasthtml-card-stack auto_adjust pattern.

    total_items is read dynamically from the scrollbar's data-total-items
    attribute so that add/delete operations are reflected without regenerating JS.
    """
    return f"""
        // === Auto-Fit Visible Rows (Overflow-Based) ===
        (function() {{
            const wrapperId = '{ids.wrapper}';
            const tableId = '{ids.table}';
            const bodyId = '{ids.rows}';
            const scrollbarId = '{ids.scrollbar_track}';
            const updateUrl = '{urls.update_viewport}';
            const _fallbackTotal = {total_items};
            const GROW_STEP = 1;

            let _visibleRows = {initial_visible};
            let _adjusting = false;
            let _adjustTimer = null;

            // Growth validation state
            let _growing = false;
            let _reverting = false;
            let _preGrowthCount = 0;
            let _preGrowthSlotIds = null;

            function _getTotalItems() {{
                const sb = document.getElementById(scrollbarId);
                if (sb) {{
                    const val = parseInt(sb.getAttribute('data-total-items'));
                    if (!isNaN(val)) return val;
                }}
                return _fallbackTotal;
            }}

            function _getWrapperHeight() {{
                const el = document.getElementById(wrapperId);
                return el ? parseInt(el.style.height) || 0 : 0;
            }}

            function _getTableHeight() {{
                const el = document.getElementById(tableId);
                return el ? el.getBoundingClientRect().height : 0;
            }}

            function _getOverflow() {{
                return _getTableHeight() - _getWrapperHeight();
            }}

            function _getAvgRowHeight() {{
                const body = document.getElementById(bodyId);
                if (!body) return 0;
                const tblRows = body.querySelectorAll('.table-row');
                if (tblRows.length === 0) return 0;
                let total = 0;
                for (const r of tblRows) total += r.getBoundingClientRect().height;
                return total / tblRows.length;
            }}

            function _postUpdate(count) {{
                _adjusting = true;
                _visibleRows = count;
                htmx.ajax('POST', updateUrl, {{
                    swap: 'none',
                    values: {{ visible_rows: count, is_auto: 'true' }}
                }});
            }}

            // --- Growth visibility helpers ---
            // Slots use display:contents, so opacity on them has no effect.
            // Hide/reveal the table-row inside each new slot.

            function _snapshotSlotIds() {{
                const body = document.getElementById(bodyId);
                if (!body) return new Set();
                const ids = new Set();
                for (const child of body.children) {{
                    if (child.id) ids.add(child.id);
                }}
                return ids;
            }}

            function _hideNewRows() {{
                if (!_preGrowthSlotIds) return;
                const body = document.getElementById(bodyId);
                if (!body) return;
                for (const child of body.children) {{
                    if (child.id && !_preGrowthSlotIds.has(child.id)) {{
                        const row = child.querySelector('.table-row');
                        if (row) row.style.opacity = '0';
                    }}
                }}
            }}

            function _revealAllRows() {{
                const body = document.getElementById(bodyId);
                if (!body) return;
                const rows = body.querySelectorAll('.table-row');
                for (const r of rows) {{
                    if (r.style.opacity === '0') r.style.removeProperty('opacity');
                }}
            }}

            // --- Core adjust logic ---

            function _runAdjust() {{
                if (_adjusting) return;

                if (_reverting) {{
                    _reverting = false;
                    return;
                }}

                if (_growing) {{
                    _validateGrowth();
                    return;
                }}

                const totalItems = _getTotalItems();
                const overflow = _getOverflow();
                const wrapperH = _getWrapperHeight();
                if (wrapperH === 0) return;

                if (overflow > 2) {{
                    // Shrink: batch-estimate rows to remove
                    const avgRowH = _getAvgRowHeight();
                    const toRemove = avgRowH > 0
                        ? Math.max(1, Math.ceil(overflow / avgRowH))
                        : 1;
                    const newCount = Math.max(1, _visibleRows - toRemove);
                    if (newCount !== _visibleRows) {{
                        _postUpdate(newCount);
                    }}
                }} else if (_visibleRows < totalItems) {{
                    // Grow: snapshot, try adding GROW_STEP
                    _preGrowthCount = _visibleRows;
                    _preGrowthSlotIds = _snapshotSlotIds();
                    _growing = true;
                    const newCount = Math.min(_visibleRows + GROW_STEP, totalItems);
                    _postUpdate(newCount);
                }}
            }}

            function _validateGrowth() {{
                const overflow = _getOverflow();
                if (overflow > 2) {{
                    // Growth caused overflow — revert to pre-growth count
                    _growing = false;
                    _reverting = true;
                    _preGrowthSlotIds = null;
                    _postUpdate(_preGrowthCount);
                }} else {{
                    // Growth fits — reveal and continue
                    _revealAllRows();
                    _growing = false;
                    _preGrowthSlotIds = null;
                    requestAnimationFrame(function() {{
                        _runAdjust();
                    }});
                }}
            }}

            // --- Entry point (debounced) ---

            window._vcAutoFit_{config.prefix} = function() {{
                clearTimeout(_adjustTimer);
                _reverting = false;
                _adjustTimer = setTimeout(function() {{
                    _runAdjust();
                }}, 200);
            }};

            // --- HTMX event handlers ---

            document.body.addEventListener('htmx:afterSwap', function() {{
                if (_growing) _hideNewRows();
            }});

            document.body.addEventListener('htmx:afterSettle', function() {{
                if (!_adjusting) return;
                _adjusting = false;
                requestAnimationFrame(function() {{
                    _runAdjust();
                }});
            }});

            // --- Initial auto-fit ---
            // ResizeObserver on wrapper catches viewport-fit's first height set
            // (resize_callback doesn't fire on initial calculation)

            const _initEl = document.getElementById(wrapperId);
            if (_initEl) {{
                const _initObserver = new ResizeObserver(function() {{
                    _initObserver.disconnect();
                    window._vcAutoFit_{config.prefix}();
                }});
                _initObserver.observe(_initEl);
            }}
        }})();
    """

# %% ../../nbs/js/auto_fit.ipynb #d4950ae4
def auto_fit_callback_name(
    config: VirtualCollectionConfig,  # Collection config (for prefix)
) -> str:  # Global JS function name
    """Get the global callback name for viewport-fit's resize_callback."""
    return f"window._vcAutoFit_{config.prefix}()"
