/**
 * virtual-scroll.js — Lightweight vanilla-JS virtual scroll helper.
 * Supports <div> containers and <tbody> tables.
 *
 * Usage:
 *   var vs = new VirtualScroll({
 *     container: document.getElementById('myTableBody'),
 *     rowHeight: 48,
 *     buffer: 5,
 *     renderRow: function(item, index) { return '<tr>...</tr>'; }
 *   });
 *   vs.setData(arrayOfItems);
 */
(function(window) {
    'use strict';

    function VirtualScroll(opts) {
        this.container = opts.container;
        this.rowHeight = opts.rowHeight || 48;
        this.buffer = opts.buffer || 5;
        this.renderRow = opts.renderRow || function() { return ''; };
        this.emptyHtml = opts.emptyHtml || '';
        this.data = [];
        this._scrollHandler = this._onScroll.bind(this);
        this._resizeHandler = this._onResize.bind(this);
        this._isTbody = this.container.tagName === 'TBODY';
        this._rafId = null;
        this._pendingRender = false;
        this._init();
    }

    VirtualScroll.prototype._init = function() {
        var c = this.container;
        if (!this._isTbody) {
            c.style.overflowY = 'auto';
            c.style.position = 'relative';
            if (!c.style.maxHeight && !c.style.height) {
                c.style.maxHeight = '60vh';
            }
        } else {
            // For tbody, scroll is on the parent element (table wrapper)
            var table = c.closest('table');
            if (table) {
                var wrapper = table.parentElement;
                if (wrapper && wrapper.classList.contains('vs-container')) {
                    wrapper.addEventListener('scroll', this._scrollHandler, { passive: true });
                }
            }
        }
        window.addEventListener('resize', this._resizeHandler, { passive: true });
    };

    VirtualScroll.prototype.setData = function(data) {
        this.data = data || [];
        this._render();
    };

    VirtualScroll.prototype.refresh = function() {
        this._render();
    };

    VirtualScroll.prototype._onScroll = function() {
        this._scheduleRender();
    };

    VirtualScroll.prototype._onResize = function() {
        this._scheduleRender();
    };

    VirtualScroll.prototype._scheduleRender = function() {
        if (this._pendingRender) return;
        this._pendingRender = true;
        var self = this;
        this._rafId = requestAnimationFrame(function() {
            self._rafId = null;
            self._pendingRender = false;
            self._render();
        });
    };

    VirtualScroll.prototype._getScrollTop = function() {
        if (this._isTbody) {
            var table = this.container.closest('table');
            if (table) {
                var wrapper = table.parentElement;
                if (wrapper && wrapper.classList.contains('vs-container')) {
                    return wrapper.scrollTop;
                }
            }
            return 0;
        }
        return this.container.scrollTop;
    };

    VirtualScroll.prototype._getContainerHeight = function() {
        if (this._isTbody) {
            var table = this.container.closest('table');
            if (table) {
                var wrapper = table.parentElement;
                if (wrapper && wrapper.classList.contains('vs-container')) {
                    return wrapper.clientHeight;
                }
            }
            return 600;
        }
        return this.container.clientHeight;
    };

    VirtualScroll.prototype._render = function() {
        var data = this.data;
        var rowH = this.rowHeight;
        var buffer = this.buffer;

        if (!data.length) {
            this.container.innerHTML = this.emptyHtml;
            return;
        }

        var scrollTop = this._getScrollTop();
        var containerHeight = this._getContainerHeight();
        var startIdx = Math.max(0, Math.floor(scrollTop / rowH) - buffer);
        var endIdx = Math.min(data.length, Math.ceil((scrollTop + containerHeight) / rowH) + buffer);

        // Build new HTML
        var html = '';
        if (this._isTbody) {
            var topSpacerHeight = startIdx * rowH;
            if (topSpacerHeight > 0) {
                html += '<tr style="height:' + topSpacerHeight + 'px;" aria-hidden="true"><td colspan="99" style="padding:0;border:0;"></td></tr>';
            }
            for (var i = startIdx; i < endIdx; i++) {
                html += this.renderRow(data[i], i);
            }
            var bottomSpacerHeight = (data.length - endIdx) * rowH;
            if (bottomSpacerHeight > 0) {
                html += '<tr style="height:' + bottomSpacerHeight + 'px;" aria-hidden="true"><td colspan="99" style="padding:0;border:0;"></td></tr>';
            }
        } else {
            for (var j = startIdx; j < endIdx; j++) {
                html += this.renderRow(data[j], j);
            }
        }

        // Use a DocumentFragment approach via innerHTML to avoid flicker:
        // Setting innerHTML on tbody is the fastest way, but we can reduce
        // perceived flicker by ensuring the container has contain: paint
        // and by batching via rAF (done in _scheduleRender).
        this.container.innerHTML = html;
    };

    VirtualScroll.prototype.destroy = function() {
        if (this._isTbody) {
            var table = this.container.closest('table');
            if (table) {
                var wrapper = table.parentElement;
                if (wrapper && wrapper.classList.contains('vs-container')) {
                    wrapper.removeEventListener('scroll', this._scrollHandler);
                }
            }
        } else {
            this.container.removeEventListener('scroll', this._scrollHandler);
        }
        window.removeEventListener('resize', this._resizeHandler);
    };

    window.VirtualScroll = VirtualScroll;
})(window);
