// ── Initialization & Event Listeners (Tables Module) ─────────────────
document.addEventListener('DOMContentLoaded', () => {
    function initializeRelationTooltips(root = document) {
        const tooltipMap = [
            ['.relation-help-compact', 'Hover any relation option for guidance. These settings control generated page placement, relation navigation, display order, and optional database constraints.'],
            ['label[for="relDispInline"], label[for="editRelDispInline"]', 'Show matching detail rows directly on the master page. Best for common child data.'],
            ['label[for="relDispPopup"], label[for="editRelDispPopup"]', 'Open detail records in a modal to keep the master page compact.'],
            ['label[for="relDispIcon"], label[for="editRelDispIcon"]', 'Show a compact icon/action for details. Useful when many relations exist.'],
            ['label[for="relShowDetailsLinks"], label[for="editRelShowDetailsLinks"]', 'Adds detail navigation from master list rows.'],
            ['.relation-link-mode-card', 'Single link groups all details together. Individual links expose each detail table separately.'],
            ['label[for="relHideDetailsNoRecords"], label[for="editRelHideDetailsNoRecords"]', 'Hide detail navigation when the master record has no child rows.'],
            ['label[for="relShowProceedLink"], label[for="editRelShowProceedLink"]', 'Adds a link from the preview area to the full detail page.'],
            ['label[for="relAddForeignKey"], label[for="editRelAddForeignKey"]', 'Creates or stores a database foreign key rule. Use only when the master field is primary or unique.'],
            ['#relOnDeleteAction, #editRelOnDeleteAction', 'Controls what happens to detail rows when a master row is deleted.'],
            ['#relOnUpdateAction, #editRelOnUpdateAction', 'Controls what happens to detail rows when the master key value changes.'],
            ['label[for="relDisplayChildCount"], label[for="editRelDisplayChildCount"]', 'Shows a count badge so users can see how many child rows exist.'],
            ['#relBadgeColor, #editRelBadgeColor', 'Choose the badge color used for child record counts.'],
            ['label[for="relHideIfEmpty"], label[for="editRelHideIfEmpty"]', 'Suppresses empty detail previews in generated pages.'],
            ['#relDisplayOrder, #editRelDisplayOrder', 'Lower numbers appear first when the same master table has multiple detail sections.'],
        ];

        function activateTooltip(el, title, placement = 'top') {
            if (!el) return;
            el.setAttribute('data-bs-toggle', 'tooltip');
            el.setAttribute('data-bs-container', 'body');
            el.setAttribute('data-bs-placement', el.getAttribute('data-bs-placement') || placement);
            el.setAttribute('data-bs-custom-class', el.getAttribute('data-bs-custom-class') || 'tables-tooltip');
            if (!el.getAttribute('title') && !el.getAttribute('data-bs-original-title')) {
                el.setAttribute('title', title);
            }
            if (window.bootstrap?.Tooltip) {
                window.bootstrap.Tooltip.getInstance(el)?.dispose();
                new window.bootstrap.Tooltip(el, { container: 'body', trigger: 'hover focus' });
            }
        }

        tooltipMap.forEach(([selector, title]) => {
            root.querySelectorAll(selector).forEach(el => activateTooltip(el, title));
        });

        root.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
            const title = el.getAttribute('title') || el.getAttribute('data-bs-original-title') || el.getAttribute('aria-label') || 'More information';
            activateTooltip(el, title);
        });

        root.querySelectorAll('.master-page-check, .edit-master-page-check').forEach(input => {
            const page = input.value ? input.value.charAt(0).toUpperCase() + input.value.slice(1) : 'selected';
            activateTooltip(input.closest('.relation-page-chip'), `Show detail records on the master ${page} page.`);
        });

        root.querySelectorAll('.detail-page-check, .edit-detail-page-check').forEach(input => {
            const page = input.value ? input.value.charAt(0).toUpperCase() + input.value.slice(1) : 'selected';
            activateTooltip(input.closest('.relation-page-chip'), `Show the master record on the detail ${page} page.`);
        });

        root.querySelectorAll('.page-check, .edit-page-check').forEach(input => {
            const page = input.value ? input.value.charAt(0).toUpperCase() + input.value.slice(1) : 'selected';
            activateTooltip(input.closest('.relation-page-chip'), `Reserve this relation setting for generated ${page} modules.`);
        });
    }

    window.initializeTablesTooltips = initializeRelationTooltips;
    initializeRelationTooltips();
    document.getElementById('relationModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));
    document.getElementById('editRelationModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));
    document.getElementById('createTableModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));
    document.getElementById('editTableModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));
    document.getElementById('createCustomViewModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));
    document.getElementById('createSqlViewModal')?.addEventListener('shown.bs.modal', e => initializeRelationTooltips(e.currentTarget));

    function makeSchemaModalDraggable(modalEl) {
        const dialog = modalEl?.querySelector('.modal-dialog.table-schema-modal');
        const handle = modalEl?.querySelector('.modal-content');
        if (!modalEl || !dialog || !handle) return;

        let offsetX = 0;
        let offsetY = 0;
        let startX = 0;
        let startY = 0;
        let resizeStartX = 0;
        let resizeStartY = 0;
        let resizeStartWidth = 0;
        let resizeStartBodyHeight = 0;
        let resizing = false;
        let resizeMode = '';
        let resizeMoved = false;
        let minX = 0;
        let maxX = 0;
        let minY = 0;
        let maxY = 0;
        let dragging = false;

        dialog.classList.add('schema-modal-draggable');
        modalEl.querySelector('.modal-header')?.setAttribute('title', 'Drag to reposition');
        if (!handle.querySelector('.schema-resize-edge')) {
            handle.insertAdjacentHTML('beforeend', `
                <span class="schema-resize-edge schema-resize-right" data-resize-mode="right" title="Drag to resize, click to expand width"></span>
                <span class="schema-resize-edge schema-resize-bottom" data-resize-mode="bottom" title="Drag to resize, click to expand height"></span>
                <span class="schema-resize-edge schema-resize-corner" data-resize-mode="corner" title="Drag to resize, click to expand"></span>
            `);
        }

        function applyTransform() {
            dialog.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
        }

        function resetPosition() {
            offsetX = 0;
            offsetY = 0;
            applyTransform();
        }

        function calculateBounds() {
            const rect = dialog.getBoundingClientRect();
            const baseLeft = rect.left - offsetX;
            const baseTop = rect.top - offsetY;
            const visibleX = Math.min(260, Math.max(160, rect.width * 0.2));
            const visibleY = Math.min(160, Math.max(80, rect.height * 0.18));
            minX = visibleX - (baseLeft + rect.width);
            maxX = window.innerWidth - visibleX - baseLeft;
            minY = visibleY - (baseTop + rect.height);
            maxY = window.innerHeight - visibleY - baseTop;
        }

        function onPointerMove(e) {
            if (!dragging) return;
            offsetX = Math.min(maxX, Math.max(minX, e.clientX - startX));
            offsetY = Math.min(maxY, Math.max(minY, e.clientY - startY));
            applyTransform();
        }

        function onPointerUp() {
            if (!dragging) return;
            dragging = false;
            dialog.classList.remove('schema-modal-dragging');
            document.removeEventListener('pointermove', onPointerMove);
            document.removeEventListener('pointerup', onPointerUp);
        }

        function bodyEl() {
            return modalEl.querySelector('.modal-body');
        }

        function resizeBounds() {
            return {
                minWidth: 980,
                maxWidth: Math.max(1040, window.innerWidth - 32),
                minBodyHeight: 260,
                maxBodyHeight: Math.max(320, window.innerHeight - 180),
            };
        }

        function onResizeMove(e) {
            if (!resizing) return;
            const bounds = resizeBounds();
            const deltaX = e.clientX - resizeStartX;
            const deltaY = e.clientY - resizeStartY;
            if (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3) resizeMoved = true;

            if (resizeMode === 'right' || resizeMode === 'corner') {
                const nextWidth = Math.min(bounds.maxWidth, Math.max(bounds.minWidth, resizeStartWidth + deltaX));
                dialog.style.width = `${nextWidth}px`;
                dialog.style.maxWidth = 'calc(100vw - 1rem)';
            }
            if (resizeMode === 'bottom' || resizeMode === 'corner') {
                const modalBody = bodyEl();
                if (modalBody) {
                    const nextHeight = Math.min(bounds.maxBodyHeight, Math.max(bounds.minBodyHeight, resizeStartBodyHeight + deltaY));
                    modalBody.style.maxHeight = `${nextHeight}px`;
                }
            }
            calculateBounds();
            offsetX = Math.min(maxX, Math.max(minX, offsetX));
            offsetY = Math.min(maxY, Math.max(minY, offsetY));
            applyTransform();
        }

        function toggleExpanded(mode) {
            const modalBody = bodyEl();
            const bounds = resizeBounds();
            const widthExpanded = dialog.classList.contains('schema-width-expanded');
            const heightExpanded = dialog.classList.contains('schema-height-expanded');

            if (mode === 'right' || mode === 'corner') {
                dialog.classList.toggle('schema-width-expanded', !widthExpanded);
                dialog.style.width = widthExpanded ? '' : `${bounds.maxWidth}px`;
                dialog.style.maxWidth = widthExpanded ? '' : 'calc(100vw - 1rem)';
            }
            if (modalBody && (mode === 'bottom' || mode === 'corner')) {
                modalBody.classList.toggle('schema-height-expanded', !heightExpanded);
                modalBody.style.maxHeight = heightExpanded ? '' : `${bounds.maxBodyHeight}px`;
            }
            calculateBounds();
            offsetX = Math.min(maxX, Math.max(minX, offsetX));
            offsetY = Math.min(maxY, Math.max(minY, offsetY));
            applyTransform();
        }

        function onResizeUp() {
            if (!resizing) return;
            resizing = false;
            dialog.classList.remove('schema-modal-resizing');
            document.removeEventListener('pointermove', onResizeMove);
            document.removeEventListener('pointerup', onResizeUp);
            if (!resizeMoved) toggleExpanded(resizeMode);
            resizeMode = '';
        }

        handle.querySelectorAll('.schema-resize-edge').forEach(edge => {
            edge.addEventListener('pointerdown', (e) => {
                resizing = true;
                resizeMoved = false;
                resizeMode = edge.dataset.resizeMode || 'corner';
                resizeStartX = e.clientX;
                resizeStartY = e.clientY;
                resizeStartWidth = dialog.getBoundingClientRect().width;
                resizeStartBodyHeight = bodyEl()?.getBoundingClientRect().height || 0;
                dialog.classList.add('schema-modal-resizing');
                document.addEventListener('pointermove', onResizeMove);
                document.addEventListener('pointerup', onResizeUp);
                e.preventDefault();
                e.stopPropagation();
            });
        });

        handle.addEventListener('pointerdown', (e) => {
            const blockedDragTarget = e.target.closest('input, select, textarea, button, a, label, .form-check, .schema-fields-wrap, .modal-footer, .btn-close, .schema-resize-edge, [data-bs-dismiss]');
            if (e.button !== 0 || blockedDragTarget) return;
            dragging = true;
            calculateBounds();
            startX = e.clientX - offsetX;
            startY = e.clientY - offsetY;
            dialog.classList.add('schema-modal-dragging');
            document.addEventListener('pointermove', onPointerMove);
            document.addEventListener('pointerup', onPointerUp);
            e.preventDefault();
        });

        modalEl.addEventListener('shown.bs.modal', resetPosition);
        modalEl.addEventListener('hidden.bs.modal', () => {
            resetPosition();
            dialog.style.width = '';
            dialog.style.maxWidth = '';
            dialog.classList.remove('schema-width-expanded', 'schema-height-expanded');
            const modalBody = bodyEl();
            if (modalBody) {
                modalBody.style.maxHeight = '';
                modalBody.classList.remove('schema-height-expanded');
            }
        });
        window.addEventListener('resize', resetPosition);
    }

    // 1. Mobile sidebar toggle
    const btnToggle = document.getElementById('btnToggleTableList');
    const leftPanel = document.querySelector('.left-panel');
    if (btnToggle && leftPanel) {
        btnToggle.addEventListener('click', () => {
            leftPanel.classList.toggle('collapsed');
            btnToggle.innerHTML = leftPanel.classList.contains('collapsed')
                ? '<i class="bi bi-list"></i> Tables'
                : '<i class="bi bi-x-lg"></i> Hide';
        });
    }

    // 2. Drop zone for dragging tables from left panel
    const dropZone = document.getElementById('selectedTablesContainer');
    if (dropZone) {
        dropZone.addEventListener('dragover', (e) => e.preventDefault());
        dropZone.addEventListener('drop', (e) => {
            e.preventDefault();
            const tableName = e.dataTransfer.getData('text/plain').trim();
            if (tableName && !window.selectedTablesSet.has(tableName)) {
                const cb = document.querySelector(`#tableList .table-check[value="${tableName}"]`);
                if (cb && !cb.checked) {
                    cb.checked = true;
                    cb.dispatchEvent(new Event('change'));
                }
            }
        });
    }

    // 3. Modal triggers (create table, custom view, SQL view)
    const createTableModal = document.getElementById('createTableModal');
    if (createTableModal) {
        makeSchemaModalDraggable(createTableModal);
        document.getElementById('btnCreateTable')?.addEventListener('click', () => new bootstrap.Modal(createTableModal).show());
        document.getElementById('btnCreateTable2')?.addEventListener('click', () => new bootstrap.Modal(createTableModal).show());
    }
    makeSchemaModalDraggable(document.getElementById('editTableModal'));
    const customViewModal = document.getElementById('createCustomViewModal');
    if (customViewModal) {
        document.getElementById('btnCustomView')?.addEventListener('click', () => new bootstrap.Modal(customViewModal).show());
    }
    const sqlViewModal = document.getElementById('createSqlViewModal');
    if (sqlViewModal) {
        document.getElementById('btnSqlView')?.addEventListener('click', () => new bootstrap.Modal(sqlViewModal).show());
    }

    // 4. Create Table Modal: add field row
    const addFieldBtn = document.getElementById('addFieldBtn');
    const fieldRows = document.getElementById('fieldRows');
    if (addFieldBtn && fieldRows) {
        const dialect = window.databaseDialect || {};
        const fieldTypes = Array.isArray(dialect.field_types) && dialect.field_types.length ? dialect.field_types : ['VARCHAR'];
        const noLengthTypes = new Set(dialect.no_length_types || []);
        const noDefaultTypes = new Set(dialect.no_value_default_types || []);
        const integerTypes = new Set(dialect.integer_types || ['INT']);
        const floatingTypes = new Set(['FLOAT', 'DOUBLE', 'REAL']);
        const enumLikeTypes = new Set(dialect.enum_types || []);
        const sizedStringTypes = new Set(dialect.sized_types || ['VARCHAR', 'CHAR']);
        const temporalTypes = new Set(dialect.temporal_types || ['DATETIME', 'TIMESTAMP']);
        const identityType = fieldTypes.includes('INT') ? 'INT' : (fieldTypes.includes('INTEGER') ? 'INTEGER' : [...integerTypes][0]);
        const defaultFieldType = fieldTypes.includes('VARCHAR') ? 'VARCHAR' : fieldTypes[0];

        function createTableRowControls(row) {
            return {
                nameInput: row.querySelector('.field-name') || row.querySelector('input[type=text]'),
                typeSelect: row.querySelector('.field-type'),
                sizeInput: row.querySelector('input[type=number]'),
                scaleInput: row.querySelector('.field-scale'),
                nullSelect: row.querySelector('.null-select'),
                defaultMode: row.querySelector('.default-mode'),
                defaultInput: row.querySelector('.field-default'),
                pkCheck: row.querySelector('.pk-check'),
                aiCheck: row.querySelector('.ai-check'),
                enumInput: row.querySelector('.enum-values'),
            };
        }

        function syncCreateDefaultControls(row) {
            const { typeSelect, nullSelect, defaultMode, defaultInput } = createTableRowControls(row);
            if (!typeSelect || !defaultMode || !defaultInput) return;

            const type = typeSelect.value.toUpperCase();
            const currentMode = defaultMode.value || 'NONE';
            const canUseCurrentTimestamp = temporalTypes.has(type);
            const isNullable = nullSelect?.value === '1';
            const requiresExpressionDefault = noDefaultTypes.has(type);

            [...defaultMode.options].forEach(option => {
                option.disabled = false;
                if (option.value === 'CURRENT_TIMESTAMP' && !canUseCurrentTimestamp) option.disabled = true;
                if (option.value === 'VALUE' && requiresExpressionDefault) option.disabled = true;
                if (option.value === 'NULL' && !isNullable) option.disabled = true;
            });

            if (defaultMode.selectedOptions[0]?.disabled) {
                defaultMode.value = currentMode === 'VALUE' && requiresExpressionDefault ? 'EXPRESSION' : 'NONE';
            }

            const mode = defaultMode.value || 'NONE';
            const needsInput = mode === 'VALUE' || mode === 'EXPRESSION';
            defaultInput.disabled = !needsInput;
            defaultInput.placeholder = mode === 'EXPRESSION' ? 'e.g., JSON_ARRAY()' : 'Default value';
            if (!needsInput) defaultInput.value = '';
        }

        function refreshCreateFieldRowIds() {
            fieldRows.querySelectorAll('.field-row').forEach((row, index) => {
                const pk = row.querySelector('.pk-check');
                const ai = row.querySelector('.ai-check');
                if (pk) {
                    pk.id = `create_pk_${index}`;
                    pk.closest('.form-check')?.querySelector('label')?.setAttribute('for', pk.id);
                }
                if (ai) {
                    ai.id = `create_ai_${index}`;
                    ai.closest('.form-check')?.querySelector('label')?.setAttribute('for', ai.id);
                }
            });
        }

        function applyTypeRules(row) {
            const { typeSelect, sizeInput, scaleInput, enumInput } = createTableRowControls(row);
            if (!typeSelect) return;

            const type = typeSelect.value.toUpperCase();
            if (enumInput) {
                enumInput.style.display = enumLikeTypes.has(type) ? 'block' : 'none';
                if (!enumLikeTypes.has(type)) enumInput.value = '';
            }
            if (scaleInput) {
                const supportsScale = type === 'DECIMAL' || type === 'NUMERIC';
                scaleInput.style.display = supportsScale ? '' : 'none';
                scaleInput.disabled = !supportsScale;
                if (supportsScale && scaleInput.value === '') scaleInput.value = '2';
            }

            syncCreateDefaultControls(row);

            if (sizeInput) {
                if (sizedStringTypes.has(type) && !sizeInput.value) {
                    sizeInput.value = '255';
                } else if (integerTypes.has(type) || floatingTypes.has(type)) {
                    sizeInput.value = '';
                } else if ((type === 'DECIMAL' || type === 'NUMERIC') && (!sizeInput.value || sizeInput.value === '255')) {
                    sizeInput.value = '10';
                }
                if (noLengthTypes.has(type) || enumLikeTypes.has(type)) {
                    sizeInput.value = '';
                    sizeInput.disabled = true;
                } else {
                    sizeInput.disabled = false;
                }
            }
        }

        function applyKeyRules(row) {
            const { typeSelect, sizeInput, nullSelect, defaultMode, defaultInput, pkCheck, aiCheck } = createTableRowControls(row);
            if (!typeSelect || !pkCheck || !aiCheck) return;

            if (aiCheck.checked) {
                pkCheck.checked = true;
                pkCheck.disabled = true;
                if (defaultMode) defaultMode.value = 'NONE';
                if (defaultInput) {
                    defaultInput.value = '';
                    defaultInput.disabled = true;
                }
            } else {
                pkCheck.disabled = false;
            }

            if (pkCheck.checked || aiCheck.checked) {
                typeSelect.value = identityType;
                if (sizeInput) {
                    sizeInput.value = '';
                    sizeInput.disabled = false;
                }
                if (nullSelect) {
                    nullSelect.value = '0';
                    nullSelect.disabled = true;
                }
            } else if (nullSelect) {
                nullSelect.disabled = false;
            }

            applyTypeRules(row);
            syncCreateDefaultControls(row);
        }

        function bindCreateFieldRow(row) {
            const typeSelect = row.querySelector('.field-type');
            const pkCheck = row.querySelector('.pk-check');
            const aiCheck = row.querySelector('.ai-check');
            if (typeSelect) {
                typeSelect.addEventListener('change', function () {
                    applyTypeRules(row);
                    applyKeyRules(row);
                });
            }
            pkCheck?.addEventListener('change', () => applyKeyRules(row));
            aiCheck?.addEventListener('change', () => applyKeyRules(row));
            row.querySelector('.null-select')?.addEventListener('change', () => syncCreateDefaultControls(row));
            row.querySelector('.default-mode')?.addEventListener('change', () => syncCreateDefaultControls(row));
            applyTypeRules(row);
            applyKeyRules(row);
        }
        addFieldBtn.addEventListener('click', () => {
            const row = fieldRows.querySelector('.field-row').cloneNode(true);
            row.querySelectorAll('input[type=text], input[type=number]').forEach(i => i.value = '');
            row.querySelectorAll('input, select').forEach(i => i.disabled = false);
            row.querySelectorAll('.pk-check, .ai-check').forEach(i => i.checked = false);
            row.querySelector('.field-type').value = defaultFieldType;
            row.querySelector('.null-select').value = '1';
            const defaultMode = row.querySelector('.default-mode');
            if (defaultMode) defaultMode.value = 'NONE';
            const enumInput = row.querySelector('.enum-values');
            if (enumInput) { enumInput.style.display = 'none'; enumInput.value = ''; }
            fieldRows.appendChild(row);
            bindCreateFieldRow(row);
            refreshCreateFieldRowIds();
            window.initializeTablesTooltips?.(row);
        });
        fieldRows.querySelectorAll('.field-row').forEach(row => bindCreateFieldRow(row));
        refreshCreateFieldRowIds();
        fieldRows.addEventListener('click', (e) => {
            if (e.target.closest('.remove-field')) {
                const rows = fieldRows.querySelectorAll('.field-row');
                if (rows.length > 1) {
                    e.target.closest('.field-row').remove();
                    refreshCreateFieldRowIds();
                }
            }
        });
    }

    // 5. Submit table creation
    const createSubmit = document.getElementById('btnCreateTableSubmit');
    if (createSubmit) {
        createSubmit.addEventListener('click', async () => {
            const tableName = document.getElementById('newTableName').value.trim();
            if (!tableName) { Swal.fire('Error', 'Table name is required.', 'warning'); return; }
            if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tableName)) {
                Swal.fire('Invalid table name', 'Use letters, numbers, and underscores. The first character must be a letter or underscore.', 'warning');
                return;
            }
            const cols = [];
            document.querySelectorAll('#fieldRows .field-row').forEach(row => {
                const name = (row.querySelector('.field-name') || row.querySelector('input[type=text]')).value.trim();
                if (!name) return;
                const type = row.querySelector('.field-type').value;
                const size = row.querySelector('input[type=number]').value || 0;
                const scale = row.querySelector('.field-scale')?.value || 0;
                const nullable = row.querySelector('.null-select')?.value === '1';
                const defaultMode = row.querySelector('.default-mode')?.value || 'NONE';
                const defaultVal = row.querySelector('.field-default')?.value ?? null;
                const isPk = row.querySelector('.pk-check').checked;
                const isAi = row.querySelector('.ai-check').checked;
                const enumValues = (type === 'ENUM' || type === 'SET') ? (row.querySelector('.enum-values')?.value || '') : '';
                cols.push({
                    name,
                    type,
                    size,
                    scale,
                    nullable,
                    default_mode: defaultMode,
                    default: defaultVal,
                    default_expression: defaultMode === 'EXPRESSION' ? defaultVal : null,
                    primary_key: isPk,
                    auto_increment: isAi,
                    enum_values: enumValues
                });
            });
            if (cols.length === 0) { Swal.fire('Error', 'Add at least one field.', 'warning'); return; }
            const invalidField = cols.find(col => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(col.name));
            if (invalidField) {
                Swal.fire('Invalid field name', `"${invalidField.name}" is not valid. Use letters, numbers, and underscores. The first character must be a letter or underscore.`, 'warning');
                return;
            }
            const fieldNameCounts = cols.reduce((counts, col) => {
                const key = col.name.toLowerCase();
                counts[key] = (counts[key] || 0) + 1;
                return counts;
            }, {});
            const duplicateField = Object.keys(fieldNameCounts).find(name => fieldNameCounts[name] > 1);
            if (duplicateField) {
                Swal.fire('Duplicate field', `The field "${duplicateField}" is listed more than once.`, 'warning');
                return;
            }
            if (cols.filter(col => col.auto_increment).length > 1) {
                Swal.fire('Auto increment', 'Only one field can use AI in a table.', 'warning');
                return;
            }
            try {
                const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/create`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ table_name: tableName, fields: cols })
                });
                const data = await res.json();
                if (data.success) {
                    Swal.fire('Success', data.message, 'success');
                    bootstrap.Modal.getInstance(createTableModal).hide();
                    location.reload();
                } else { Swal.fire('Error', data.message, 'error'); }
            } catch (err) { Swal.fire('Error', 'Request failed.', 'error'); }
        });
    }

    function isValidViewName(name) {
        return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
    }

    function validateViewDefinition(viewName, sql) {
        if (!viewName || !sql) return 'View name and SQL are required.';
        if (!isValidViewName(viewName)) return 'View name can contain letters, numbers, and underscores only. It must start with a letter or underscore.';

        const normalized = sql.trim().replace(/;\s*$/, '');
        if (normalized.includes(';')) return 'Only one SELECT statement is allowed.';
        if (!/^\s*(SELECT|WITH)\b/i.test(normalized)) return 'Only SELECT statements can be used to create a view.';
        if (/\bINTO\b/i.test(normalized) || /\b(INSERT|UPDATE|DELETE|MERGE|DROP|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE|CALL|COPY|VACUUM|PRAGMA|ATTACH|DETACH)\b/i.test(normalized)) {
            return 'Views cannot contain data-changing or administrative statements.';
        }
        return '';
    }

    function escapeHtml(value) {
        return String(value ?? '').replace(/[&<>"']/g, char => ({
            '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'
        }[char]));
    }

    function setButtonLoading(button, loading, label) {
        if (!button) return;
        if (loading) {
            button.dataset.originalHtml = button.innerHTML;
            button.disabled = true;
            button.innerHTML = `<span class="spinner-border spinner-border-sm me-1"></span>${label}`;
        } else {
            button.disabled = false;
            if (button.dataset.originalHtml) button.innerHTML = button.dataset.originalHtml;
        }
    }

    async function previewViewSql(sql, resultDiv, button) {
        const validationMessage = validateViewDefinition('preview_view', sql);
        if (validationMessage) {
            Swal.fire('Invalid SQL', validationMessage, 'warning');
            return;
        }

        resultDiv.innerHTML = '<div class="text-center py-2"><div class="spinner-border spinner-border-sm"></div></div>';
        setButtonLoading(button, true, 'Previewing');
        try {
            const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/sql-preview`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ sql: sql })
            });
            const data = await res.json();
            if (data.success) {
                const rows = Array.isArray(data.rows) ? data.rows : [];
                const columns = Array.isArray(data.columns) ? data.columns : [];
                if (columns.length === 0) {
                    resultDiv.innerHTML = '<div class="alert alert-info small py-1">Query returned no columns.</div>';
                    return;
                }
                if (rows.length === 0) {
                    resultDiv.innerHTML = '<div class="alert alert-info small py-1">Query is valid and returned 0 rows.</div>';
                    return;
                }

                let html = '<table class="table table-sm table-bordered small mb-0"><thead><tr>';
                columns.forEach(col => html += `<th>${escapeHtml(col)}</th>`);
                html += '</tr></thead><tbody>';
                rows.forEach(row => {
                    html += '<tr>';
                    columns.forEach(col => html += `<td>${escapeHtml(row[col])}</td>`);
                    html += '</tr>';
                });
                html += '</tbody></table>';
                resultDiv.innerHTML = html;
            } else {
                resultDiv.innerHTML = `<div class="alert alert-danger small py-1">${escapeHtml(data.message || 'Preview failed.')}</div>`;
            }
        } catch (err) {
            resultDiv.innerHTML = '<div class="alert alert-danger small py-1">Preview failed.</div>';
        } finally {
            setButtonLoading(button, false);
        }
    }

    // 6. Custom View submit
    const customViewSubmit = document.getElementById('btnCreateCustomViewSubmit');
    const customPreviewBtn = document.getElementById('btnPreviewCustomView');
    if (customPreviewBtn) {
        customPreviewBtn.addEventListener('click', async () => {
            await previewViewSql(
                document.getElementById('customViewSQL').value.trim(),
                document.getElementById('viewResult'),
                customPreviewBtn
            );
        });
    }
    if (customViewSubmit) {
        customViewSubmit.addEventListener('click', async () => {
            const viewName = document.getElementById('customViewName').value.trim();
            const sql = document.getElementById('customViewSQL').value.trim();
            const validationMessage = validateViewDefinition(viewName, sql);
            if (validationMessage) { Swal.fire('Invalid view', validationMessage, 'warning'); return; }
            setButtonLoading(customViewSubmit, true, 'Creating');
            try {
                const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/custom-view`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ view_name: viewName, sql: sql })
                });
                const data = await res.json();
                if (data.success) {
                    Swal.fire('Success', data.message, 'success');
                    bootstrap.Modal.getInstance(customViewModal).hide();
                    location.reload();
                } else { Swal.fire('Error', data.message, 'error'); }
            } catch (err) { Swal.fire('Error', 'Failed to create view.', 'error'); }
            finally { setButtonLoading(customViewSubmit, false); }
        });
    }

    // 7. SQL View: Preview SQL
    const previewBtn = document.getElementById('btnPreviewSqlView');
    if (previewBtn) {
        previewBtn.addEventListener('click', async () => {
            const sql = document.getElementById('sqlViewCode').value.trim();
            await previewViewSql(sql, document.getElementById('sqlPreviewResult'), previewBtn);
        });
    }

    // 8. SQL View submit
    const sqlViewSubmit = document.getElementById('btnCreateSqlViewSubmit');
    if (sqlViewSubmit) {
        sqlViewSubmit.addEventListener('click', async () => {
            const viewName = document.getElementById('sqlViewName').value.trim();
            const sql = document.getElementById('sqlViewCode').value.trim();
            const validationMessage = validateViewDefinition(viewName, sql);
            if (validationMessage) { Swal.fire('Invalid view', validationMessage, 'warning'); return; }
            setButtonLoading(sqlViewSubmit, true, 'Creating');
            try {
                const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/sql-view`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ view_name: viewName, sql: sql })
                });
                const data = await res.json();
                if (data.success) {
                    Swal.fire('Success', data.message, 'success');
                    bootstrap.Modal.getInstance(sqlViewModal).hide();
                    location.reload();
                } else { Swal.fire('Error', data.message, 'error'); }
            } catch (err) { Swal.fire('Error', 'Failed to create view.', 'error'); }
            finally { setButtonLoading(sqlViewSubmit, false); }
        });
    }

    // 9. Edit modal: Delete table
    const deleteTableBtn = document.getElementById('btnDeleteTable');
    if (deleteTableBtn) {
        deleteTableBtn.addEventListener('click', () => {
            const tableName = (window.editTableOriginalName || document.getElementById('editTableNameInput').value).trim();
            if (!tableName) return;
            Swal.fire({
                title: 'Delete table?',
                text: `You are about to permanently delete the table "${tableName}". This action cannot be undone.`,
                icon: 'warning', showCancelButton: true, confirmButtonColor: window.tablesThemeColor?.('--tables-danger-accent'), confirmButtonText: 'Yes, delete'
            }).then(async (result) => {
                if (result.isConfirmed) {
                    try {
                        const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/delete/${tableName}`, { method: 'POST' });
                        const data = await res.json();
                        if (data.success) {
                            Swal.fire('Deleted', data.message, 'success');
                            bootstrap.Modal.getInstance(document.getElementById('editTableModal')).hide();
                            location.reload();
                        } else { Swal.fire('Error', data.message, 'error'); }
                    } catch (err) { Swal.fire('Error', 'Failed to delete.', 'error'); }
                }
            });
        });
    }

    // 10. Link Mode toggle (master‑detail)
    const linkModeBtn = document.getElementById('btnLinkMode');
    if (linkModeBtn) {
        linkModeBtn.addEventListener('click', function () {
            window.linkMode = !window.linkMode;
            if (window.linkMode) {
                this.classList.remove('btn-outline-info');
                this.classList.add('btn-info', 'text-white');
                this.innerHTML = '<i class="bi bi-link-45deg me-1"></i> Link Mode ON';
                window.bootstrap?.Tooltip?.getInstance(this)?.dispose();
                this.setAttribute('title', 'Link Mode is active. Click to turn it off after creating or canceling a relationship.');
                window.initializeTablesTooltips?.(this.parentElement || document);
                Swal.fire({ title: 'Link Mode active', text: 'Click a field on one card, then a field on another card to create a relation.', icon: 'info', toast: true, position: 'top-end', timer: 4000, showConfirmButton: false });
            } else {
                this.classList.remove('btn-info', 'text-white');
                this.classList.add('btn-outline-info');
                this.innerHTML = '<i class="bi bi-link-45deg me-1"></i> Link Tables';
                window.bootstrap?.Tooltip?.getInstance(this)?.dispose();
                this.setAttribute('title', 'Turn on link mode to connect a master field to a detail field and create a relationship.');
                window.initializeTablesTooltips?.(this.parentElement || document);
                window.linkSource = null;
                document.querySelectorAll('.field-list-item').forEach(el => {
                    el.classList.remove('link-source-field', 'highlight-match');
                });
            }
        });
    }

    // 11. Field click during link mode (relation creation)
    const relationsContainer = document.getElementById('selectedTablesContainer');
    if (relationsContainer) {
        relationsContainer.addEventListener('click', function (e) {
            if (!window.linkMode) return;
            const fieldEl = e.target.closest('.field-list-item');
            if (!fieldEl) return;
            const table = fieldEl.dataset.table;
            const field = fieldEl.dataset.field;
            if (!table || !field) return;
            if (!window.linkSource) {
                window.linkSource = { table, field, dataType: fieldEl.dataset.type };
                fieldEl.classList.add('link-source-field');
                document.querySelectorAll('.field-list-item').forEach(el => {
                    if (el.dataset.table !== table && el.dataset.type === fieldEl.dataset.type) {
                        el.classList.add('highlight-match');
                    }
                });
                Swal.fire({ title: 'Source selected', text: 'Now click a field on another table\'s card.', icon: 'info', toast: true, position: 'top-end', timer: 3000, showConfirmButton: false });
            } else if (window.linkSource.table === table) {
                Swal.fire('Same table', 'Please select a field from a DIFFERENT table.', 'warning');
            } else {
                const target = { table, field };
                openRelationModal(window.linkSource, target);
                window.linkSource = null;
                document.querySelectorAll('.field-list-item').forEach(el => {
                    el.classList.remove('link-source-field');
                    el.classList.remove('highlight-match');
                });
            }
        });
    }

    // 12. Save relation modal
    const saveRelationBtn = document.getElementById('btnSaveRelation');
    if (saveRelationBtn) {
        saveRelationBtn.addEventListener('click', async () => {
            const payload = {
                master_table: document.getElementById('relMasterTable').value,
                detail_table: document.getElementById('relDetailTable').value,
                master_field: document.getElementById('relMasterField').value,
                detail_field: document.getElementById('relDetailField').value,
                display_option: document.querySelector('input[name="relDisplayOption"]:checked')?.value || 'inline',
                add_foreign_key: document.getElementById('relAddForeignKey').checked,
                on_delete_action: document.getElementById('relOnDeleteAction').value,
                on_update_action: document.getElementById('relOnUpdateAction').value,
                show_details_links: document.getElementById('relShowDetailsLinks').checked,
                details_link_mode: document.querySelector('input[name="relDetailsLinkMode"]:checked')?.value || 'single',
                hide_details_no_records: document.getElementById('relHideDetailsNoRecords').checked,
                show_proceed_link: document.getElementById('relShowProceedLink').checked,
                hide_if_empty: document.getElementById('relHideIfEmpty').checked,
                display_child_count: document.getElementById('relDisplayChildCount').checked,
                count_badge_color: document.getElementById('relBadgeColor').value,
                master_pages: Array.from(document.querySelectorAll('#relationModal .master-page-check:checked')).map(cb => cb.value),
                detail_pages: Array.from(document.querySelectorAll('#relationModal .detail-page-check:checked')).map(cb => cb.value),
                display_order: Number.parseInt(document.getElementById('relDisplayOrder')?.value || '10', 10) || 10,
                where: (document.getElementById('relWhere')?.value || '').trim(),
                pages: Array.from(document.querySelectorAll('#relationModal .page-check:checked')).map(cb => cb.value)
            };
            payload.show_master_on_detail = payload.detail_pages.length > 0;
            try {
                const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/relations`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
                });
                const data = await res.json();
                if (data.success) {
                    Swal.fire('Success', 'Relation created.', 'success');
                    bootstrap.Modal.getInstance(document.getElementById('relationModal')).hide();
                    loadAndDrawRelations();
                    window.linkMode = false;
                    const linkBtn = document.getElementById('btnLinkMode');
                    if (linkBtn) {
                        linkBtn.classList.remove('btn-info', 'text-white');
                        linkBtn.classList.add('btn-outline-info');
                        linkBtn.innerHTML = '<i class="bi bi-link-45deg me-1"></i> Link Tables';
                        window.bootstrap?.Tooltip?.getInstance(linkBtn)?.dispose();
                        linkBtn.setAttribute('title', 'Turn on link mode to connect a master field to a detail field and create a relationship.');
                        window.initializeTablesTooltips?.(linkBtn.parentElement || document);
                    }
                    window.linkSource = null;
                    document.querySelectorAll('.field-list-item').forEach(el => {
                        el.classList.remove('link-source-field', 'highlight-match');
                    });
                } else { Swal.fire('Error', data.message, 'error'); }
            } catch (err) { Swal.fire('Error', 'Failed to save relation.', 'error'); }
        });
    }

    // 13. Double‑click relation arrow to edit/delete
    const relationsSvg = document.getElementById('relationsSvg');
    if (relationsSvg) {
        relationsSvg.addEventListener('dblclick', (e) => {
            const target = e.target.closest('[data-rel-id]');
            if (!target || !target.dataset.relId) return;
            const rel = window.allRelations.find(r => r.id === target.dataset.relId);
            if (!rel) return;
            openEditRelationModal(rel);
        });
    }

    // 14. Update relation modal
    const updateRelationBtn = document.getElementById('btnUpdateRelation');
    if (updateRelationBtn) {
        updateRelationBtn.addEventListener('click', async () => {
            const id = document.getElementById('editRelId').value;
            const payload = {
                display_option: document.querySelector('input[name="editRelDisplayOption"]:checked')?.value || 'inline',
                master_field: document.getElementById('editRelMasterField').value,
                detail_field: document.getElementById('editRelDetailField').value,
                add_foreign_key: document.getElementById('editRelAddForeignKey').checked,
                on_delete_action: document.getElementById('editRelOnDeleteAction').value,
                on_update_action: document.getElementById('editRelOnUpdateAction').value,
                show_details_links: document.getElementById('editRelShowDetailsLinks').checked,
                details_link_mode: document.querySelector('input[name="editRelDetailsLinkMode"]:checked')?.value || 'single',
                hide_details_no_records: document.getElementById('editRelHideDetailsNoRecords').checked,
                show_proceed_link: document.getElementById('editRelShowProceedLink').checked,
                hide_if_empty: document.getElementById('editRelHideIfEmpty').checked,
                display_child_count: document.getElementById('editRelDisplayChildCount').checked,
                count_badge_color: document.getElementById('editRelBadgeColor').value,
                master_pages: Array.from(document.querySelectorAll('#editRelationModal .edit-master-page-check:checked')).map(cb => cb.value),
                detail_pages: Array.from(document.querySelectorAll('#editRelationModal .edit-detail-page-check:checked')).map(cb => cb.value),
                display_order: Number.parseInt(document.getElementById('editRelDisplayOrder')?.value || '10', 10) || 10,
                where: (document.getElementById('editRelWhere')?.value || '').trim(),
                pages: Array.from(document.querySelectorAll('#editRelationModal .edit-page-check:checked')).map(cb => cb.value)
            };
            payload.show_master_on_detail = payload.detail_pages.length > 0;
            try {
                const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/relations/update/${id}`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
                });
                const data = await res.json();
                if (data.success) {
                    Swal.fire('Updated', 'Relation updated.', 'success');
                    bootstrap.Modal.getInstance(document.getElementById('editRelationModal')).hide();
                    loadAndDrawRelations();
                } else { Swal.fire('Error', data.message, 'error'); }
            } catch (err) { Swal.fire('Error', 'Failed to update.', 'error'); }
        });
    }

    // 15. Delete relation
    const deleteRelationBtn = document.getElementById('btnDeleteRelation');
    if (deleteRelationBtn) {
        deleteRelationBtn.addEventListener('click', () => {
            const id = document.getElementById('editRelId').value;
            Swal.fire({
                title: 'Delete this relation?',
                text: 'The foreign key constraint will also be dropped from the database.',
                icon: 'warning', showCancelButton: true, confirmButtonColor: window.tablesThemeColor?.('--tables-danger-accent'), confirmButtonText: 'Yes, delete'
            }).then(async (result) => {
                if (result.isConfirmed) {
                    try {
                        const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/relations/delete/${id}`, { method: 'POST' });
                        const data = await res.json();
                        if (data.success) {
                            Swal.fire('Deleted', data.message, 'success');
                            bootstrap.Modal.getInstance(document.getElementById('editRelationModal')).hide();
                            loadAndDrawRelations();
                        } else { Swal.fire('Error', data.message, 'error'); }
                    } catch (err) { Swal.fire('Error', 'Failed to delete.', 'error'); }
                }
            });
        });
    }

    // 16. Toggle badge color pickers
    const relDisplayChildCount = document.getElementById('relDisplayChildCount');
    if (relDisplayChildCount) {
        relDisplayChildCount.addEventListener('change', function () {
            const colorRow = document.getElementById('relBadgeColorRow');
            if (colorRow) colorRow.style.display = this.checked ? 'flex' : 'none';
        });
    }
    const editRelDisplayChildCount = document.getElementById('editRelDisplayChildCount');
    if (editRelDisplayChildCount) {
        editRelDisplayChildCount.addEventListener('change', function () {
            const colorRow = document.getElementById('editRelBadgeColorRow');
            if (colorRow) colorRow.style.display = this.checked ? 'flex' : 'none';
        });
    }

    // 17. Auto‑check PK when AI is checked in edit modal (global listener)
    document.addEventListener('change', function (e) {
        if (e.target.matches('#editFieldRows .ai-check')) {
            const row = e.target.closest('.field-row');
            const pkCheck = row.querySelector('.pk-check');
            if (pkCheck) {
                if (e.target.checked) { pkCheck.checked = true; pkCheck.disabled = true; }
                else { pkCheck.disabled = false; }
            }
        }
        if (e.target.matches('#editFieldRows .pk-check')) {
            const row = e.target.closest('.field-row');
            const aiCheck = row.querySelector('.ai-check');
            if (aiCheck && aiCheck.checked && !e.target.checked) { e.target.checked = true; }
        }
    });

    // 18. Initial render
    if (typeof window.allTables !== 'undefined') {
        renderTableList(window.allTables);
    } else {
        console.warn('allTables not defined, cannot render table list');
    }
    document.querySelectorAll('.table-check').forEach(cb => {
        if (window.selectedTablesSet.has(cb.value)) cb.checked = true;
    });
    renderSelectedCards();
    window.initializeTablesTooltips?.(document.getElementById('selectedTablesContainer') || document);
    setTimeout(() => loadAndDrawRelations(), 200);
    loadAndDrawBadges();


    // ── Override functions to trigger auto‑save ──────────────────────
    // Save references to original functions
    const originalHandleTableToggle = window.handleTableToggle;
    const originalHandleDragEnd = window.handleDragEnd;
    const originalResolveOverlaps = window.resolveOverlaps;

    // Override handleTableToggle
    if (originalHandleTableToggle) {
        window.handleTableToggle = function (tableName, isChecked) {
            originalHandleTableToggle(tableName, isChecked);
            window.autoSaveTablesState();
        };
    }

    // Override handleDragEnd (defined in cards.js)
    if (originalHandleDragEnd) {
        window.handleDragEnd = function (e) {
            originalHandleDragEnd(e);
            window.autoSaveTablesState();
        };
    }

    // Override resolveOverlaps (defined in cards.js)
    if (originalResolveOverlaps) {
        window.resolveOverlaps = function (card) {
            originalResolveOverlaps(card);
            window.autoSaveTablesState();
        };
    }
});
