class Table {
    constructor(options = {}) {
        this.options = {
            containerId: options.containerId || null,
            id: options.id || this.generateId(),
            title: options.title || null,
            description: options.description || null,

            headerColumns: options.headerColumns || [],
            headerRows: options.headerRows || [],
            rowGroups: options.rowGroups || null,

            cellType: options.cellType || 'selectable',
            columnTypes: options.columnTypes || null,
            inputType: options.inputType || 'text',
            selectionMode: options.selectionMode || 'single',

            columnWidths: options.columnWidths || null,
            rowHeaderWidth: options.rowHeaderWidth || 'auto',

            striped: options.striped !== undefined ? options.striped : true,
            hoverable: options.hoverable !== undefined ? options.hoverable : true,
            bordered: options.bordered !== undefined ? options.bordered : false,
            compact: options.compact !== undefined ? options.compact : false,

            validation: options.validation || null,
            showValidationErrors: options.showValidationErrors !== undefined ? options.showValidationErrors : true,

            onChange: options.onChange || null,
            onValidate: options.onValidate || null,

            initialData: options.initialData || null
        };

        this.container = null;
        this.tableElement = null;
        this.data = this.initializeData();
        this.validationErrors = {};

        this.init();
    }

    generateId() {
        return 'table-' + Math.random().toString(36).substring(2, 11);
    }

    getCellType(colIndex) {

        if (this.options.columnTypes && Array.isArray(this.options.columnTypes)) {
            return this.options.columnTypes[colIndex] || this.options.cellType;
        }
        return this.options.cellType;
    }

    initializeData() {
        const data = {};

        if (this.options.initialData) {
            return JSON.parse(JSON.stringify(this.options.initialData));
        }

        this.options.headerRows.forEach((rowHeader, rowIndex) => {
            data[rowIndex] = {};
            this.options.headerColumns.forEach((colHeader, colIndex) => {
                const cellType = this.getCellType(colIndex);
                if (cellType === 'selectable') {
                    data[rowIndex][colIndex] = false;
                } else {
                    data[rowIndex][colIndex] = '';
                }
            });
        });

        return data;
    }

    init() {
        if (this.options.containerId) {
            this.container = document.getElementById(this.options.containerId);
            if (this.container) {
                this.render();
            }
        }
    }

    render() {
        if (!this.container) return;

        const wrapper = document.createElement('div');
        wrapper.className = 'table-component-wrapper';
        wrapper.id = this.options.id + '-wrapper';

        if (this.options.title) {
            const title = document.createElement('div');
            title.className = 'table-component-title';
            title.textContent = this.options.title;
            wrapper.appendChild(title);
        }

        if (this.options.description) {
            const description = document.createElement('div');
            description.className = 'table-component-description';
            description.textContent = this.options.description;
            wrapper.appendChild(description);
        }

        const tableContainer = document.createElement('div');
        tableContainer.className = 'table-component';
        tableContainer.id = this.options.id;

        if (this.options.striped) tableContainer.classList.add('striped');
        if (this.options.hoverable) tableContainer.classList.add('hoverable');
        if (this.options.bordered) tableContainer.classList.add('bordered');
        if (this.options.compact) tableContainer.classList.add('compact');

        const table = document.createElement('table');
        table.setAttribute('role', 'grid');

        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');

        const cornerCell = document.createElement('th');
        cornerCell.className = 'header-row';
        headerRow.appendChild(cornerCell);

        this.options.headerColumns.forEach((header, colIndex) => {
            const th = document.createElement('th');

            if (typeof header === 'string') {
                th.textContent = header;
            } else if (typeof header === 'object' && header.lines) {

                th.className = 'multi-line-header';
                header.lines.forEach((line, index) => {
                    if (index > 0) {
                        th.appendChild(document.createElement('br'));
                    }
                    th.appendChild(document.createTextNode(line));
                });
            } else if (typeof header === 'object' && header.responsive) {

                th.className = 'responsive-header';

                const fullSpan = document.createElement('span');
                fullSpan.setAttribute('data-full-text', 'true');
                fullSpan.textContent = header.responsive.full;
                th.appendChild(fullSpan);

                const shortSpan = document.createElement('span');
                shortSpan.setAttribute('data-short-text', 'true');
                shortSpan.textContent = header.responsive.short;
                th.appendChild(shortSpan);

                const letterSpan = document.createElement('span');
                letterSpan.setAttribute('data-letter-text', 'true');
                letterSpan.textContent = header.responsive.letter;
                th.appendChild(letterSpan);
            }

            if (this.options.columnWidths && this.options.columnWidths[colIndex]) {
                th.style.width = this.options.columnWidths[colIndex];
            }

            headerRow.appendChild(th);
        });

        thead.appendChild(headerRow);
        table.appendChild(thead);

        const tbody = document.createElement('tbody');

        if (this.options.rowGroups && this.options.rowGroups.length > 0) {

            this.options.rowGroups.forEach(group => {

                const categoryRow = document.createElement('tr');
                categoryRow.className = 'category-row';

                const categoryCell = document.createElement('td');
                categoryCell.className = 'category-header';
                categoryCell.textContent = group.category;
                categoryCell.setAttribute('colspan', this.options.headerColumns.length + 1);
                categoryRow.appendChild(categoryCell);

                tbody.appendChild(categoryRow);

                group.rows.forEach(rowIndex => {
                    const rowHeader = this.options.headerRows[rowIndex];
                    const row = document.createElement('tr');
                    row.className = 'grouped-row';

                    const rowHeaderCell = document.createElement('td');
                    rowHeaderCell.className = 'row-header';
                    rowHeaderCell.textContent = rowHeader;

                    if (this.options.rowHeaderWidth) {
                        rowHeaderCell.style.width = this.options.rowHeaderWidth;
                    }

                    row.appendChild(rowHeaderCell);

                    this.options.headerColumns.forEach((colHeader, colIndex) => {
                        const cell = this.createCell(rowIndex, colIndex);
                        row.appendChild(cell);
                    });

                    row.setAttribute('data-row-index', rowIndex);

                    tbody.appendChild(row);
                });
            });
        } else {

            this.options.headerRows.forEach((rowHeader, rowIndex) => {
                const row = document.createElement('tr');

                const rowHeaderCell = document.createElement('td');
                rowHeaderCell.className = 'row-header';
                rowHeaderCell.textContent = rowHeader;

                if (this.options.rowHeaderWidth) {
                    rowHeaderCell.style.width = this.options.rowHeaderWidth;
                }

                row.appendChild(rowHeaderCell);

                this.options.headerColumns.forEach((colHeader, colIndex) => {
                    const cell = this.createCell(rowIndex, colIndex);
                    row.appendChild(cell);
                });

                row.setAttribute('data-row-index', rowIndex);

                tbody.appendChild(row);
            });
        }

        table.appendChild(tbody);
        tableContainer.appendChild(table);
        wrapper.appendChild(tableContainer);

        this.container.innerHTML = '';
        this.container.appendChild(wrapper);

        this.tableElement = tableContainer;

        if (this.options.hoverable) {
            this.setupColumnHover();
        }
    }

    setupColumnHover() {
        if (!this.tableElement) return;

        const cells = this.tableElement.querySelectorAll('td.selectable, td.input-cell');

        cells.forEach(cell => {
            const colIndex = Array.from(cell.parentElement.children).indexOf(cell);

            cell.addEventListener('mouseenter', () => {
                const allRows = this.tableElement.querySelectorAll('tbody tr');
                allRows.forEach(row => {
                    const targetCell = row.children[colIndex];
                    if (targetCell) {
                        targetCell.setAttribute('data-column-hover', 'true');
                    }
                });
            });

            cell.addEventListener('mouseleave', () => {
                const allCells = this.tableElement.querySelectorAll('[data-column-hover="true"]');
                allCells.forEach(c => c.removeAttribute('data-column-hover'));
            });
        });
    }

    createCell(rowIndex, colIndex) {
        const cell = document.createElement('td');
        const cellType = this.getCellType(colIndex);

        if (this.options.columnWidths && this.options.columnWidths[colIndex]) {
            cell.style.width = this.options.columnWidths[colIndex];
        }

        if (cellType === 'selectable') {
            cell.className = 'selectable';
            cell.setAttribute('tabindex', '0');
            cell.setAttribute('role', 'gridcell');
            cell.setAttribute('aria-label', `${this.options.headerRows[rowIndex]} - ${this.getColumnLabel(colIndex)}`);

            if (this.data[rowIndex][colIndex]) {
                cell.classList.add('selected');
                cell.setAttribute('aria-selected', 'true');
            } else {
                cell.setAttribute('aria-selected', 'false');
            }

            cell.addEventListener('click', () => this.handleCellClick(rowIndex, colIndex, cell));

            cell.addEventListener('keydown', (e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    this.handleCellClick(rowIndex, colIndex, cell);
                }
            });
        } else {
            cell.className = 'input-cell';

            const input = this.options.inputType === 'textarea'
                ? document.createElement('textarea')
                : document.createElement('input');

            if (this.options.inputType !== 'textarea') {
                input.type = 'text';
            }

            input.value = this.data[rowIndex][colIndex] || '';
            input.setAttribute('aria-label', `${this.options.headerRows[rowIndex]} - ${this.getColumnLabel(colIndex)}`);

            input.addEventListener('input', (e) => {
                this.handleInputChange(rowIndex, colIndex, e.target.value);

                if (this.options.inputType === 'textarea') {
                    this.autoGrowTextarea(e.target);
                }
            });

            if (this.options.inputType === 'textarea') {
                setTimeout(() => this.autoGrowTextarea(input), 0);
            }

            cell.appendChild(input);
        }

        return cell;
    }

    getColumnLabel(colIndex) {
        const header = this.options.headerColumns[colIndex];
        if (typeof header === 'string') {
            return header;
        } else if (typeof header === 'object' && header.lines) {
            return header.lines[0];
        }
        return '';
    }

    autoGrowTextarea(textarea) {
        textarea.style.height = 'auto';
        const newHeight = Math.max(40, textarea.scrollHeight);
        textarea.style.height = newHeight + 'px';

        const cell = textarea.parentElement;
        if (cell) {
            cell.style.height = 'auto';
        }
    }

    handleCellClick(rowIndex, colIndex, cell) {
        if (this.options.selectionMode === 'single') {

            const row = cell.parentElement;
            const cells = row.querySelectorAll('td.selectable');
            cells.forEach(c => {
                c.classList.remove('selected');
                c.setAttribute('aria-selected', 'false');
            });

            Object.keys(this.data[rowIndex]).forEach(key => {
                this.data[rowIndex][key] = false;
            });
        }

        const isSelected = cell.classList.toggle('selected');
        cell.setAttribute('aria-selected', isSelected ? 'true' : 'false');
        this.data[rowIndex][colIndex] = isSelected;

        if (isSelected) {
            this.clearRowValidationError(rowIndex);
        }

        if (this.options.onChange) {
            this.options.onChange(this.getData());
        }
    }

    handleInputChange(rowIndex, colIndex, value) {
        this.data[rowIndex][colIndex] = value;

        if (value && value.trim() !== '') {
            this.clearCellValidationError(rowIndex, colIndex);
        }

        if (this.options.onChange) {
            this.options.onChange(this.getData());
        }
    }

    getData() {
        return JSON.parse(JSON.stringify(this.data));
    }

    setData(data) {
        this.data = data;
        this.render();
    }

    clear() {
        this.data = this.initializeData();
        this.render();
    }

    validate() {
        this.validationErrors = {};
        let isValid = true;

        if (this.options.validation) {
            const customErrors = this.options.validation(this.getData());
            if (customErrors && Object.keys(customErrors).length > 0) {
                this.validationErrors = customErrors;
                isValid = false;
            }
        }

        if (this.options.cellType === 'selectable') {
            isValid = this.validateSelectableCells() && isValid;
        } else if (this.options.cellType === 'input') {
            isValid = this.validateInputCells() && isValid;
        }

        if (this.options.showValidationErrors) {
            this.updateValidationUI();
        }

        if (this.options.onValidate) {
            return this.options.onValidate(this.getData());
        }

        return isValid;
    }

    validateSelectableCells() {
        let isValid = true;

        this.options.headerRows.forEach((rowHeader, rowIndex) => {
            const hasSelection = Object.values(this.data[rowIndex]).some(val => val === true);

            if (!hasSelection) {
                this.validationErrors[`row-${rowIndex}`] = `${rowHeader} requires a selection`;
                isValid = false;
            }
        });

        return isValid;
    }

    validateInputCells() {
        let isValid = true;

        this.options.headerRows.forEach((rowHeader, rowIndex) => {
            this.options.headerColumns.forEach((colHeader, colIndex) => {
                const value = this.data[rowIndex][colIndex];
                const isEmpty = !value || (typeof value === 'string' && value.trim() === '');

                if (isEmpty) {
                    const cellKey = `cell-${rowIndex}-${colIndex}`;
                    this.validationErrors[cellKey] = `${rowHeader} - ${colHeader} is required`;
                    isValid = false;
                }
            });
        });

        return isValid;
    }

    updateValidationUI() {
        if (!this.tableElement) return;

        this.tableElement.querySelectorAll('tbody tr').forEach(row => {
            row.classList.remove('validation-error', 'validation-success');
            row.setAttribute('data-validation-error', '');
        });

        Object.entries(this.validationErrors).forEach(([key, error]) => {
            if (key.startsWith('row-')) {

                const rowIndex = parseInt(key.split('-')[1]);
                const row = this.tableElement.querySelector(`tbody tr[data-row-index="${rowIndex}"]`);
                if (row) {
                    row.classList.add('validation-error');
                    row.setAttribute('data-validation-error', error);
                }
            } else if (key.startsWith('cell-')) {

                const [, rowIndex] = key.split('-').map(Number);
                const row = this.tableElement.querySelector(`tbody tr[data-row-index="${rowIndex}"]`);
                if (row) {
                    row.classList.add('validation-error');
                    row.setAttribute('data-validation-error', error);
                }
            }
        });

        if (Object.keys(this.validationErrors).length === 0) {
            this.tableElement.querySelectorAll('tbody tr').forEach(row => {
                row.classList.add('validation-success');
            });
        }
    }

    clearRowValidationError(rowIndex) {
        const rowKey = `row-${rowIndex}`;
        if (this.validationErrors[rowKey]) {
            delete this.validationErrors[rowKey];

            if (this.tableElement) {
                const row = this.tableElement.querySelector(`tbody tr[data-row-index="${rowIndex}"]`);
                if (row) {
                    row.classList.remove('validation-error', 'validation-success');
                    row.removeAttribute('data-validation-error');
                }
            }
        }
    }

    clearCellValidationError(rowIndex, colIndex) {
        const cellKey = `cell-${rowIndex}-${colIndex}`;
        if (this.validationErrors[cellKey]) {
            delete this.validationErrors[cellKey];

            if (this.tableElement) {
                const row = this.tableElement.querySelector(`tbody tr[data-row-index="${rowIndex}"]`);
                if (row) {
                    row.classList.remove('validation-error', 'validation-success');
                    row.removeAttribute('data-validation-error');
                }
            }
        }
    }

    clearValidationErrors() {
        this.validationErrors = {};
        if (this.tableElement) {
            this.tableElement.querySelectorAll('tbody tr').forEach(row => {
                row.classList.remove('validation-error', 'validation-success');
                row.removeAttribute('data-validation-error');
            });
        }
    }

    getValidationErrors() {
        return JSON.parse(JSON.stringify(this.validationErrors));
    }

    setLoading(isLoading) {
        if (this.tableElement) {
            if (isLoading) {
                this.tableElement.classList.add('loading');
            } else {
                this.tableElement.classList.remove('loading');
            }
        }
    }

    setDisabled(isDisabled) {
        if (this.tableElement) {
            if (isDisabled) {
                this.tableElement.classList.add('disabled');
            } else {
                this.tableElement.classList.remove('disabled');
            }
        }
    }

    exportToCSV() {
        const rows = [];

        const headerRow = ['', ...this.options.headerColumns];
        rows.push(headerRow);

        this.options.headerRows.forEach((rowHeader, rowIndex) => {
            const row = [rowHeader];
            this.options.headerColumns.forEach((colHeader, colIndex) => {
                const value = this.data[rowIndex][colIndex];

                row.push(typeof value === 'boolean' ? (value ? 'X' : '') : value);
            });
            rows.push(row);
        });

        const csvContent = rows.map(row =>
            row.map(cell => {
                const cellStr = String(cell);
                if (cellStr.includes(',') || cellStr.includes('"') || cellStr.includes('\n')) {
                    return '"' + cellStr.replace(/"/g, '""') + '"';
                }
                return cellStr;
            }).join(',')
        ).join('\n');

        return csvContent;
    }

    getElement() {
        return this.tableElement;
    }

    destroy() {
        if (this.container) {
            this.container.innerHTML = '';
        }
    }
}