var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { MelodicComponent } from '@melodicdev/core';
import { tableTemplate } from './table.template.js';
import { tableStyles } from './table.styles.js';
import { VirtualScroller } from '../../../utils/virtual-scroll/index.js';
// Track elements that receive ml:row-click listeners before custom element upgrade.
// The template engine adds event listeners via addEventListener on un-upgraded elements
// (before the constructor runs), so we intercept at the prototype level to catch them.
const _rowClickTargets = new WeakSet();
const _origAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
    if (type === 'ml:row-click') {
        _rowClickTargets.add(this);
    }
    _origAddEventListener.call(this, type, listener, options);
};
/**
 * ml-table - Data table with sorting, selection, and custom cell rendering
 *
 * @example
 * ```html
 * <ml-table .columns=${columns} .rows=${rows} striped hoverable></ml-table>
 * ```
 *
 * @fires ml:sort - Emitted when a sortable column header is clicked. Detail: { key, direction }
 * @fires ml:select - Emitted when row selection changes. Detail: { selectedRows, allSelected }
 * @fires ml:row-click - Emitted when a row is clicked. Detail: { row, index }
 *
 * @slot footer - Content for the table footer area (e.g. pagination)
 * @slot header-actions - Actions placed in the header next to title/description
 */
let TableComponent = class TableComponent {
    constructor() {
        /** Whether a row-click listener is attached (auto-detected) */
        this._rowClickable = false;
        /** Whether the footer slot has content */
        this.hasFooter = false;
        /** Whether the header-actions slot has content */
        this.hasHeaderActions = false;
        /** Enable row selection via checkboxes */
        this.selectable = false;
        /** Alternating row backgrounds */
        this.striped = false;
        /** Highlight rows on hover */
        this.hoverable = true;
        /** Sticky table header */
        this.stickyHeader = false;
        /** Table size */
        this.size = 'md';
        /** Table header title */
        this.tableTitle = '';
        /** Table header description */
        this.description = '';
        /** Enable virtual scrolling (renders only visible rows) */
        this.virtual = false;
        /** Column definitions */
        this.columns = [];
        /** Row data */
        this.rows = [];
        /** Currently sorted column key */
        this.sortKey = '';
        /** Current sort direction */
        this.sortDirection = 'asc';
        /** Indices of selected rows */
        this.selectedIndices = [];
        // ── Virtual scroll state ─────────────────────────────────────────────────────
        this.startIndex = 0;
        this.endIndex = 50;
        // ── Private ──────────────────────────────────────────────────────────────────
        this._scroller = new VirtualScroller();
        this._viewport = null;
        this.isRowSelected = (index) => {
            return this.selectedIndices.includes(index);
        };
        // ── Event handlers ────────────────────────────────────────────────────────────
        this.handleSort = (column) => {
            if (!column.sortable)
                return;
            if (this.sortKey === column.key) {
                this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
            }
            else {
                this.sortKey = column.key;
                this.sortDirection = 'asc';
            }
            this.selectedIndices = [];
            this._scroller.invalidate();
            this.elementRef.dispatchEvent(new CustomEvent('ml:sort', {
                bubbles: true,
                composed: true,
                detail: { key: this.sortKey, direction: this.sortDirection }
            }));
        };
        this.handleSelectAll = () => {
            if (this.allSelected) {
                this.selectedIndices = [];
            }
            else {
                this.selectedIndices = this.rows.map((_, i) => i);
            }
            this.emitSelect();
        };
        this.handleSelectRow = (index, event) => {
            event.stopPropagation();
            if (this.selectedIndices.includes(index)) {
                this.selectedIndices = this.selectedIndices.filter(i => i !== index);
            }
            else {
                this.selectedIndices = [...this.selectedIndices, index];
            }
            this.emitSelect();
        };
        this.handleRowClick = (row, index) => {
            this.elementRef.dispatchEvent(new CustomEvent('ml:row-click', {
                bubbles: true,
                composed: true,
                detail: { row, index }
            }));
        };
    }
    // ── Row height by size ────────────────────────────────────────────────────────
    get rowHeight() {
        return this.size === 'sm' ? 36 : 44;
    }
    // ── Lifecycle ─────────────────────────────────────────────────────────────────
    onPropertyChange(name, _oldVal, _newVal) {
        if (name === 'rows' || name === 'columns') {
            this._scroller.invalidate();
        }
    }
    onInit() {
        this._rowClickable = _rowClickTargets.has(this.elementRef);
    }
    onCreate() {
        const shadow = this.elementRef.shadowRoot;
        if (!shadow)
            return;
        shadow.querySelectorAll('slot').forEach(slot => {
            slot.addEventListener('slotchange', () => {
                const name = slot.getAttribute('name');
                if (name === 'footer') {
                    this.hasFooter = slot.assignedNodes().length > 0;
                }
                else if (name === 'header-actions') {
                    this.hasHeaderActions = slot.assignedNodes().length > 0;
                }
            });
        });
        this._attachScroller();
    }
    onRender() {
        this._attachScroller();
        // Compute initial end index when viewport height not yet known
        if (this.virtual && this._viewport && this._viewport.clientHeight === 0 && this.sortedRows.length > 0) {
            const approxEnd = Math.min(this.sortedRows.length, Math.ceil(600 / this.rowHeight) + 6);
            if (approxEnd !== this.endIndex) {
                this.endIndex = approxEnd;
            }
        }
        if (!this.virtual) {
            const total = this.sortedRows.length;
            if (this.endIndex !== total)
                this.endIndex = total;
        }
    }
    onDestroy() {
        this._scroller.detach();
        this._viewport = null;
    }
    // ── Private helpers ───────────────────────────────────────────────────────────
    _attachScroller() {
        if (this._viewport)
            return; // already attached
        const shadow = this.elementRef.shadowRoot;
        if (!shadow)
            return;
        this._viewport = shadow.querySelector('.ml-table__wrapper');
        if (!this._viewport)
            return;
        this._scroller.attach(this._viewport, {
            rowHeight: () => this.rowHeight,
            itemCount: () => this.sortedRows.length,
            onUpdate: (start, end) => { this.startIndex = start; this.endIndex = end; },
            enabled: () => this.virtual,
        });
    }
    // ── Data ──────────────────────────────────────────────────────────────────────
    /** Rows sorted by current sort key/direction */
    get sortedRows() {
        if (!this.sortKey)
            return this.rows;
        const key = this.sortKey;
        const dir = this.sortDirection === 'asc' ? 1 : -1;
        return [...this.rows].sort((a, b) => {
            const aVal = a[key];
            const bVal = b[key];
            if (aVal === undefined || aVal === null) {
                return (bVal === undefined || bVal === null) ? 0 : 1;
            }
            if (bVal === undefined || bVal === null)
                return -1;
            if (typeof aVal === 'number' && typeof bVal === 'number')
                return (aVal - bVal) * dir;
            return String(aVal).localeCompare(String(bVal)) * dir;
        });
    }
    get visibleRows() {
        if (!this.virtual)
            return this.sortedRows;
        return this.sortedRows.slice(this.startIndex, this.endIndex);
    }
    get topSpacerHeight() {
        return this.virtual ? this.startIndex * this.rowHeight : 0;
    }
    get bottomSpacerHeight() {
        if (!this.virtual)
            return 0;
        return Math.max(0, (this.sortedRows.length - this.endIndex) * this.rowHeight);
    }
    get colCount() {
        return this.columns.length + (this.selectable ? 1 : 0);
    }
    get allSelected() {
        return this.rows.length > 0 && this.selectedIndices.length === this.rows.length;
    }
    get someSelected() {
        return this.selectedIndices.length > 0 && !this.allSelected;
    }
    emitSelect() {
        this.elementRef.dispatchEvent(new CustomEvent('ml:select', {
            bubbles: true,
            composed: true,
            detail: { selectedRows: this.selectedIndices, allSelected: this.allSelected }
        }));
    }
};
TableComponent = __decorate([
    MelodicComponent({
        selector: 'ml-table',
        template: tableTemplate,
        styles: tableStyles,
        attributes: ['selectable', 'striped', 'hoverable', 'size', 'table-title', 'description', 'sticky-header', 'virtual']
    })
], TableComponent);
export { TableComponent };
