// ── UI Rendering, Loading, Modals ─────────────────────────────────────
async function loadTableConfig(tableName) {
    currentTableName = tableName;
    document.getElementById('selectedTableLabel').textContent = tableName;

    // Fetch table columns
    try {
        const res = await fetch(`${baseUrl}/project/${projectSlug}/tables/details/${tableName}`);
        const data = await res.json();
        if (data.success) tableColumns = data.columns;
    } catch (err) { tableColumns = []; }

    // Check local presets
    if (fieldsConfig && fieldsConfig[tableName]) {
        currentConfig = normalizeConfig(fieldsConfig[tableName]);
        currentDevice = currentConfig.activeDevice || 'desktop';
        renderGrid();
        return;
    }

    // Load from server
    try {
        const res = await fetch(`${baseUrl}/project/${projectSlug}/fields/load/${tableName}`);
        const data = await res.json();
        if (data.success) {
            currentConfig = normalizeConfig(data.config);
            currentDevice = currentConfig.activeDevice || 'desktop';
            renderGrid();
        }
    } catch (err) {
        currentConfig = { activeDevice: 'desktop', devices: {}, searchSettings: {} };
        currentDevice = 'desktop';
        renderGrid();
    }
}

// PHP encodes an empty associative array as a JSON array `[]`. If we let
// searchSettings/devices stay a JS array, adding string keys to it and then
// JSON.stringify-ing SILENTLY DROPS those keys — so nothing ever saves. Coerce
// per-key maps to plain objects so saves round-trip correctly.
function normalizeConfig(cfg) {
    if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) cfg = {};
    ['devices', 'searchSettings'].forEach(k => {
        if (!cfg[k] || typeof cfg[k] !== 'object' || Array.isArray(cfg[k])) {
            cfg[k] = Object.assign({}, cfg[k]);   // [] → {}, keeps any string keys
        }
    });
    return cfg;
}

// Page-type metadata: full label + icon so the grid shows a clear icon + name
// with a tooltip instead of a cryptic single letter.
const PAGE_META = {
    list:   { label: 'List',   icon: 'bi-list-ul' },
    add:    { label: 'Add',    icon: 'bi-plus-circle' },
    edit:   { label: 'Edit',   icon: 'bi-pencil-square' },
    view:   { label: 'View',   icon: 'bi-eye' },
    print:  { label: 'Print',  icon: 'bi-printer' },
    export: { label: 'Export', icon: 'bi-box-arrow-down' },
    import: { label: 'Import', icon: 'bi-box-arrow-in-up' },
    cards:  { label: 'Cards',  icon: 'bi-grid-3x2-gap' },
    search: { label: 'Search', icon: 'bi-search' },
};
const DEVICE_META = [
    ['desktop', 'Desktop', 'bi-laptop'],
    ['tablet',  'Tablet',  'bi-tablet-landscape'],
    ['mobile',  'Mobile',  'bi-phone'],
];

function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c])); }

function getSearchIndicatorState(settings) {
    const searchable = !settings || settings.searchable !== false;
    if (!searchable) return { className: 'is-off', label: 'Off', icon: 'bi-sliders2' };
    if (settings.filterPanel) return { className: 'is-filter', label: 'Filter', icon: 'bi-funnel' };
    return { className: 'is-on', label: 'On', icon: 'bi-sliders2' };
}

// Keep the row indicator in sync after the modal is saved. This intentionally
// updates only the relevant pill: rebuilding the matrix would replace every
// checkbox and listener while a developer is working through a large schema.
function updateSearchSettingsIndicator(fieldName) {
    const button = Array.from(document.querySelectorAll('.search-settings-btn'))
        .find(candidate => candidate.dataset.field === fieldName);
    if (!button) return;

    const settings = currentConfig?.searchSettings?.[fieldName] || { searchable: true };
    const state = getSearchIndicatorState(settings);
    button.classList.remove('is-filter', 'is-on', 'is-off');
    button.classList.add(state.className);
    button.innerHTML = `<i class="bi ${state.icon}" aria-hidden="true"></i><span>${state.label}</span>`;
    button.setAttribute('aria-label', `Search and filter settings for ${fieldName}: ${state.label}`);
    button.title = `Search and filter settings for ${fieldName}: ${state.label}`;
}

function renderGrid() {
    const container = document.getElementById('fieldGridContainer');
    if (tableColumns.length === 0) {
        container.innerHTML = '<div class="text-center py-5 text-muted"><div class="spinner-border spinner-border-sm"></div><div class="small mt-2">Loading fields…</div></div>';
        return;
    }

    // Drop the empty-state placeholder utility classes (py-5 etc.) so the grid
    // isn't pushed down by 3rem of leftover padding.
    container.classList.remove('text-center', 'py-5', 'text-muted');

    const deviceFields = (currentConfig.devices && currentConfig.devices[currentDevice]) || {};
    const searchSettings = currentConfig.searchSettings || {};

    // ── Device segmented tabs (centered) ──
    let html = '<div class="fld-device-bar"><div class="fld-device-tabs" role="tablist" aria-label="Device">';
    DEVICE_META.forEach(([d, lbl, ic]) => {
        const on = currentDevice === d;
        html += `<button type="button" class="fld-device-tab${on ? ' active' : ''}" data-device="${d}" role="tab" aria-selected="${on}"><i class="bi ${ic}"></i><span>${lbl}</span></button>`;
    });
    html += '</div></div>';
    html += `<p class="fld-hint"><i class="bi bi-info-circle"></i> Toggle where each field appears. Use the header box to select a whole column, the field box to select a whole row.</p>`;

    // ── Grid ──
    html += '<div class="fld-grid-wrap"><table class="fld-grid" id="fieldsGridTable"><thead><tr>';
    html += `<th class="fld-th-field"><label class="fld-chk-wrap" title="Select every field & page"><input type="checkbox" class="fld-chk fld-chk-master"><span>Field</span></label></th>`;
    pageTypes.forEach(pt => {
        const m = PAGE_META[pt] || { label: pt.charAt(0).toUpperCase() + pt.slice(1), icon: 'bi-square' };
        html += `<th class="fld-th-page" title="${esc(m.label)} page — tick to toggle this column for all fields">`
            + `<span class="fld-th-ic"><i class="bi ${m.icon}"></i></span>`
            + `<span class="fld-th-lbl">${esc(m.label)}</span>`
            + `<input type="checkbox" class="fld-chk fld-chk-col" data-page="${pt}" aria-label="Toggle ${esc(m.label)} for all fields">`
            + `</th>`;
    });
    html += '<th class="fld-th-search">Search</th></tr></thead><tbody>';

    tableColumns.forEach(col => {
        const name = col.COLUMN_NAME;
        const field = deviceFields[name] || defaultFieldDefaults;
        const ss = searchSettings[name] || { searchable: true };
        html += `<tr data-field="${esc(name)}">`;
        html += `<td class="fld-td-field"><label class="fld-chk-wrap" title="Select every page for ${esc(name)}">`
            + `<input type="checkbox" class="fld-chk fld-chk-row" data-field="${esc(name)}">`
            + `<span class="fld-fname">${esc(name)}</span> <span class="fld-ftype">${esc(col.COLUMN_TYPE)}</span></label></td>`;
        pageTypes.forEach(pt => {
            const m = PAGE_META[pt] || { label: pt };
            const checked = field[pt] ? 'checked' : '';
            html += `<td class="fld-td-page"><input class="fld-chk field-check" type="checkbox" data-field="${esc(name)}" data-page="${pt}" ${checked} title="${esc(m.label)}: ${esc(name)}"></td>`;
        });
        const searchState = getSearchIndicatorState(ss);
        html += `<td class="fld-td-search"><button type="button" class="fld-search-pill ${searchState.className} search-settings-btn" data-field="${esc(name)}" title="Search and filter settings for ${esc(name)}: ${searchState.label}" aria-label="Search and filter settings for ${esc(name)}: ${searchState.label}"><i class="bi ${searchState.icon}" aria-hidden="true"></i><span>${searchState.label}</span></button></td>`;
        html += `</tr>`;
    });
    html += '</tbody></table></div>';
    container.innerHTML = html;

    bindGridEvents(container);
    refreshBulkStates(container);
}

function bindGridEvents(container) {
    container.querySelectorAll('.fld-device-tab').forEach(btn => {
        btn.addEventListener('click', () => {
            if (btn.dataset.device === currentDevice) return;
            currentDevice = btn.dataset.device;
            renderGrid();
            autoSaveCurrentState();
        });
    });
    container.querySelectorAll('.field-check').forEach(cb => {
        cb.addEventListener('change', () => { refreshBulkStates(container); autoSaveCurrentState(); });
    });
    // column select-all
    container.querySelectorAll('.fld-chk-col').forEach(cb => {
        cb.addEventListener('change', () => {
            container.querySelectorAll(`.field-check[data-page="${cb.dataset.page}"]`).forEach(x => { x.checked = cb.checked; });
            refreshBulkStates(container); autoSaveCurrentState();
        });
    });
    // row select-all
    container.querySelectorAll('.fld-chk-row').forEach(cb => {
        cb.addEventListener('change', () => {
            const f = cb.dataset.field;
            container.querySelectorAll(`.field-check[data-field="${CSS.escape(f)}"]`).forEach(x => { x.checked = cb.checked; });
            refreshBulkStates(container); autoSaveCurrentState();
        });
    });
    // master select-all
    const master = container.querySelector('.fld-chk-master');
    if (master) master.addEventListener('change', () => {
        container.querySelectorAll('.field-check').forEach(x => { x.checked = master.checked; });
        refreshBulkStates(container); autoSaveCurrentState();
    });
    container.querySelectorAll('.search-settings-btn').forEach(btn => {
        btn.addEventListener('click', () => openSearchSettingsModal(btn.dataset.field));
    });
}

// Reflect the checked/indeterminate state of the row / column / master boxes
// from the individual page checkboxes.
function refreshBulkStates(container) {
    const all = Array.from(container.querySelectorAll('.field-check'));
    const tri = (cb, on, total) => { if (!cb) return; cb.checked = total > 0 && on === total; cb.indeterminate = on > 0 && on < total; };
    container.querySelectorAll('.fld-chk-col').forEach(cb => {
        const g = all.filter(x => x.dataset.page === cb.dataset.page);
        tri(cb, g.filter(x => x.checked).length, g.length);
    });
    container.querySelectorAll('.fld-chk-row').forEach(cb => {
        const g = all.filter(x => x.dataset.field === cb.dataset.field);
        tri(cb, g.filter(x => x.checked).length, g.length);
    });
    tri(container.querySelector('.fld-chk-master'), all.filter(x => x.checked).length, all.length);
}

function openSearchSettingsModal(fieldName) {
    document.getElementById('searchSettingsField').value = fieldName;
    document.getElementById('searchFieldName').textContent = fieldName;
    const ss = (currentConfig && currentConfig.searchSettings && currentConfig.searchSettings[fieldName]) || {
        searchable: true, alwaysOnPanel: false, advancedSearch: false,
        allFields: true, filterPanel: false, required: false,
        defaultOption: 'contains', caseSensitive: false, saveSearch: false, highlight: false
    };
    document.getElementById('ssSearchable').checked = ss.searchable ?? true;
    document.getElementById('ssAlwaysOnPanel').checked = ss.alwaysOnPanel ?? false;
    document.getElementById('ssAdvancedSearch').checked = ss.advancedSearch ?? false;
    document.getElementById('ssAllFields').checked = ss.allFields ?? true;
    document.getElementById('ssFilterPanel').checked = ss.filterPanel ?? false;
    document.getElementById('ssRequired').checked = ss.required ?? false;
    document.getElementById('ssDefaultOption').value = ss.defaultOption ?? 'contains';
    document.getElementById('ssCaseSensitive').checked = ss.caseSensitive ?? false;
    document.getElementById('ssSaveSearch').checked = ss.saveSearch ?? false;
    document.getElementById('ssHighlight').checked = ss.highlight ?? false;
    new bootstrap.Modal(document.getElementById('searchSettingsModal')).show();
}

window.loadTableConfig = loadTableConfig;
window.renderGrid = renderGrid;
window.openSearchSettingsModal = openSearchSettingsModal;
window.updateSearchSettingsIndicator = updateSearchSettingsIndicator;
