account.js

import { firebaseAuth, signOut, onAuthStateChanged } from '/static/js/firebase.js';
import { setTheme, getCustomThemes, saveCustomThemes } from '/static/js/utilities/theme.js';
import { LOCAL_MODE } from '/static/js/utilities/constants.js';

// ── Back button: close drawer if embedded, navigate otherwise ──────────────
document.getElementById('accountBackBtn').addEventListener('click', () => {
    if (window !== window.top) {
        window.parent.closeAccountDrawer?.();
    } else {
        window.location.href = '/app';
    }
});
const BUILTIN = new Set(['auto', 'light', 'dark']);

const COLOR_VARS = [
    { group: 'Accent',   var: '--accent',            label: 'Primary',    defaults: { dark: '#e8ff47', light: '#525600' } },
    { group: 'Accent',   var: '--accent2',           label: 'Secondary',  defaults: { dark: '#47c8ff', light: '#006eb5' } },
    { group: 'Base',     var: '--bg',                label: 'Background', defaults: { dark: '#0d0d0f', light: '#f3f4f7' } },
    { group: 'Base',     var: '--surface',           label: 'Surface',    defaults: { dark: '#141417', light: '#ffffff' } },
    { group: 'Base',     var: '--surface2',          label: 'Surface 2',  defaults: { dark: '#1c1c21', light: '#eeeef3' } },
    { group: 'Base',     var: '--border',            label: 'Border',     defaults: { dark: '#2a2a32', light: '#dddde8' } },
    { group: 'Base',     var: '--text',              label: 'Text',       defaults: { dark: '#e8e8ee', light: '#0d0d1a' } },
    { group: 'Base',     var: '--muted',             label: 'Muted',      defaults: { dark: '#6b6b7a', light: '#606280' } },
    { group: 'Waveform', var: '--waveform',          label: 'Unplayed',   defaults: { dark: '#3a3a48', light: '#525600' } },
    { group: 'Waveform', var: '--waveform-progress', label: 'Played',     defaults: { dark: '#e8ff47', light: '#c4c5d6' } },
    { group: 'Status',   var: '--danger',            label: 'Danger',     defaults: { dark: '#ff7777', light: '#c41818' } },
    { group: 'Status',   var: '--success',           label: 'Success',    defaults: { dark: '#47ff8a', light: '#1a7a42' } },
    { group: 'Status',   var: '--rec',               label: 'Recording',  defaults: { dark: '#ff4444', light: '#d42020' } },
];

/**
 * Resolves the active theme to 'light' or 'dark' (custom themes return their base).
 * @returns {'light'|'dark'}
 */
function resolvedBase() {
    const stored = localStorage.getItem('theme') || 'auto';
    if (stored === 'auto')  return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
    if (stored === 'light') return 'light';
    if (stored === 'dark')  return 'dark';
    return getCustomThemes().find(t => t.id === stored)?.base ?? 'dark';
}

/**
 * Returns the current theme's saved color map.
 * @returns {object} map of CSS variable names to color values
 */
function getCustomColors() {
    const stored = localStorage.getItem('theme') || 'auto';
    if (BUILTIN.has(stored)) {
        try { return JSON.parse(localStorage.getItem('custom-colors-' + resolvedBase()) || '{}'); } catch { return {}; }
    }
    return { ...(getCustomThemes().find(t => t.id === stored)?.colors ?? {}) };
}

/**
 * Persists `colors` to the right storage location for the active theme.
 * @param {object} colors - map of CSS variable names to color values
 */
function saveCurrentColors(colors) {
    const stored = localStorage.getItem('theme') || 'auto';
    if (BUILTIN.has(stored)) {
        localStorage.setItem('custom-colors-' + resolvedBase(), JSON.stringify(colors));
    } else {
        const themes = getCustomThemes();
        const idx    = themes.findIndex(t => t.id === stored);
        if (idx >= 0) { themes[idx].colors = colors; saveCustomThemes(themes); }
    }
    syncThemeToServer();
}

// ── Tab switching ──────────────────────────────────────────────────────────
const tabs   = document.querySelectorAll('.account-tab[data-tab]');
const panels = document.querySelectorAll('.account-panel');

/**
 * Activates the named tab and its corresponding panel.
 * @param {string} target - The tab identifier matching a data-tab attribute.
 */
function switchTab(target) {
    tabs.forEach(t   => t.classList.toggle('active', t.dataset.tab === target));
    panels.forEach(p => p.classList.toggle('active', p.id === 'tab-' + target));
}

tabs.forEach(tab => {
    tab.addEventListener('click', () => switchTab(tab.dataset.tab));
});

// Switch to a tab specified in the URL (?tab=subscription) or via postMessage
const _urlTab = new URLSearchParams(window.location.search).get('tab');
if (_urlTab) switchTab(_urlTab);

window.addEventListener('message', (e) => {
    if (e.data?.type === 'switch-tab' && e.data.tab) switchTab(e.data.tab);
});

// ── Logout ─────────────────────────────────────────────────────────────────
document.getElementById('accountLogoutBtn')?.addEventListener('click', async () => {
    if (firebaseAuth) await signOut(firebaseAuth);
    window.location.href = '/';
});

// ── Theme switching ─────────────────────────────────────────────────────────
/**
 * Activates a theme and refreshes all theme-dependent UI.
 * @param {string} themeId - The theme ID to activate.
 */
function activateTheme(themeId) {
    setTheme(themeId);
    updateThemeBtns();
    renderCustomThemes();
    renderColorVars();
    updateColorSectionLabel();
    syncThemeToServer();
}

const themeBtns = document.querySelectorAll('.theme-btn');
/** Syncs the active state of the built-in theme buttons with the stored preference. */
function updateThemeBtns() {
    const current = localStorage.getItem('theme') || 'auto';
    themeBtns.forEach(b => b.classList.toggle('active', b.dataset.themeValue === current));
}
themeBtns.forEach(btn => btn.addEventListener('click', () => activateTheme(btn.dataset.themeValue)));
updateThemeBtns();

// ── Custom theme list ───────────────────────────────────────────────────────
/** Re-renders the custom theme buttons in the theme selector group. */
function renderCustomThemes() {
    const group   = document.getElementById('themeBtnGroup');
    const addBtn  = document.getElementById('newThemeBtn');
    const themes  = getCustomThemes();
    const current = localStorage.getItem('theme') || 'auto';

    // Remove previously rendered custom theme elements
    group.querySelectorAll('.theme-btn-custom').forEach(el => el.remove());

    // Insert custom themes before the + button
    themes.forEach(t => {
        const wrap = document.createElement('span');
        wrap.className      = 'theme-btn-custom' + (t.id === current ? ' active' : '');
        wrap.dataset.themeId = t.id;

        const nameBtn = document.createElement('button');
        nameBtn.className   = 'theme-btn-custom__name';
        nameBtn.textContent = t.name;
        nameBtn.addEventListener('click', () => activateTheme(t.id));

        wrap.append(nameBtn);

        if (!t.bundled) {
            const delBtn = document.createElement('button');
            delBtn.className   = 'theme-btn-custom__del';
            delBtn.title       = 'Delete theme';
            delBtn.innerHTML = '<span class="icon icon-close" style="width:11px;height:11px;"></span>';
            delBtn.addEventListener('click', e => { e.stopPropagation(); deleteCustomTheme(t.id); });
            wrap.append(delBtn);
        }
        group.insertBefore(wrap, addBtn);
    });
}

/**
 * Deletes a custom theme by ID and falls back to 'dark' if it was active.
 * @param {string} id - The ID of the custom theme to delete.
 */
function deleteCustomTheme(id) {
    saveCustomThemes(getCustomThemes().filter(t => t.id !== id));
    if ((localStorage.getItem('theme') || 'auto') === id) activateTheme('dark');
    else { renderCustomThemes(); syncThemeToServer(); }
}

/**
 * Creates a new custom theme based on the current color state and activates it.
 * @param {string} name - Display name for the new theme.
 */
function createCustomTheme(name) {
    const base   = resolvedBase();
    const saved  = getCustomColors();
    const colors = {};
    COLOR_VARS.forEach(v => { colors[v.var] = saved[v.var] || v.defaults[base]; });
    const theme  = { id: String(Date.now()), name: name.trim(), base, colors };
    const all    = getCustomThemes();
    all.push(theme);
    saveCustomThemes(all);
    activateTheme(theme.id);
}

// ── New theme form ──────────────────────────────────────────────────────────
document.getElementById('newThemeBtn').addEventListener('click', () => {
    document.getElementById('newThemeForm').style.display = 'flex';
    document.getElementById('newThemeName').focus();
});
document.getElementById('newThemeCancel').addEventListener('click', () => {
    document.getElementById('newThemeForm').style.display = 'none';
    document.getElementById('newThemeName').value = '';
});
document.getElementById('newThemeCreate').addEventListener('click', () => {
    const name = document.getElementById('newThemeName').value.trim();
    if (!name) return;
    createCustomTheme(name);
    document.getElementById('newThemeForm').style.display = 'none';
    document.getElementById('newThemeName').value = '';
});
document.getElementById('newThemeName').addEventListener('keydown', e => {
    if (e.key === 'Enter')  document.getElementById('newThemeCreate').click();
    if (e.key === 'Escape') document.getElementById('newThemeCancel').click();
});

// ── Import / export ─────────────────────────────────────────────────────────
/**
 * Triggers a browser download of the given theme object as a JSON file.
 * @param {{name: string, base: string, colors: object}} themeObj - Theme data to export.
 */
function downloadThemeJson(themeObj) {
    const json = JSON.stringify({ name: themeObj.name, base: themeObj.base, colors: themeObj.colors }, null, 2);
    const url  = URL.createObjectURL(new Blob([json], { type: 'application/json' }));
    const a    = Object.assign(document.createElement('a'), {
        href: url, download: themeObj.name.replace(/[^a-z0-9]/gi, '-').toLowerCase() + '.json',
    });
    a.click();
    URL.revokeObjectURL(url);
}

document.getElementById('exportThemeBtn').addEventListener('click', () => {
    const stored = localStorage.getItem('theme') || 'auto';
    if (!BUILTIN.has(stored)) {
        const t = getCustomThemes().find(t => t.id === stored);
        if (t) return downloadThemeJson(t);
    }
    // Built-in: snapshot all current colors (override or default)
    const base   = resolvedBase();
    const saved  = getCustomColors();
    const colors = {};
    COLOR_VARS.forEach(v => { colors[v.var] = saved[v.var] || v.defaults[base]; });
    downloadThemeJson({ name: { auto: 'Auto', light: 'Light', dark: 'Dark' }[stored] ?? stored, base, colors });
});

document.getElementById('importThemeInput').addEventListener('change', e => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = ev => {
        try {
            const data = JSON.parse(ev.target.result);
            if (!data.colors || typeof data.colors !== 'object') throw new Error();
            const theme = {
                id:     String(Date.now()),
                name:   typeof data.name === 'string' && data.name ? data.name : 'Imported Theme',
                base:   data.base === 'light' ? 'light' : 'dark',
                colors: data.colors,
            };
            const all = getCustomThemes();
            all.push(theme);
            saveCustomThemes(all);
            activateTheme(theme.id);
        } catch { alert('Invalid theme file.'); }
        e.target.value = '';
    };
    reader.readAsText(file);
});

// ── Color customization ─────────────────────────────────────────────────────
/** Updates the color section heading to reflect the currently active theme name. */
function updateColorSectionLabel() {
    const stored = localStorage.getItem('theme') || 'auto';
    const name   = BUILTIN.has(stored)
        ? ({ auto: 'Auto', light: 'Light', dark: 'Dark' }[stored])
        : (getCustomThemes().find(t => t.id === stored)?.name ?? stored);
    document.getElementById('colorSectionLabel').textContent = `Colors — ${name}`;
}

/** Renders the color variable editor rows grouped by category. */
function renderColorVars() {
    const container = document.getElementById('colorVarsContainer');
    const colors    = getCustomColors();
    const base      = resolvedBase();
    const groups    = {};
    COLOR_VARS.forEach(v => { (groups[v.group] ??= []).push(v); });

    container.innerHTML = '';
    for (const [groupName, vars] of Object.entries(groups)) {
        const groupEl  = document.createElement('div');
        groupEl.className = 'settings-group';

        const labelEl  = document.createElement('div');
        labelEl.className   = 'settings-label';
        labelEl.textContent = groupName;
        groupEl.appendChild(labelEl);

        const listEl = document.createElement('div');
        listEl.className = 'color-var-list';

        vars.forEach(v => {
            const saved    = colors[v.var];
            const isCustom = !!saved;

            const item   = document.createElement('div');
            item.className = 'color-var-item';

            const swatch = document.createElement('input');
            swatch.type      = 'color';
            swatch.className = 'color-swatch';
            swatch.value     = saved || v.defaults[base];
            swatch.title     = v.var;

            const nameEl = document.createElement('span');
            nameEl.className   = 'color-var-label';
            nameEl.textContent = v.label;

            const resetBtn = document.createElement('button');
            resetBtn.className   = 'color-var-reset' + (isCustom ? ' color-var-reset--active' : '');
            resetBtn.title       = 'Reset to default';
            resetBtn.textContent = '↺';

            swatch.addEventListener('input', () => {
                const c = getCustomColors();
                c[v.var] = swatch.value;
                saveCurrentColors(c);
                document.documentElement.style.setProperty(v.var, swatch.value);
                resetBtn.classList.add('color-var-reset--active');
            });

            resetBtn.addEventListener('click', () => {
                const c = getCustomColors();
                delete c[v.var];
                saveCurrentColors(c);
                document.documentElement.style.removeProperty(v.var);
                swatch.value = v.defaults[resolvedBase()];
                resetBtn.classList.remove('color-var-reset--active');
            });

            item.append(swatch, nameEl, resetBtn);
            listEl.appendChild(item);
        });

        groupEl.appendChild(listEl);
        container.appendChild(groupEl);
    }
}

renderCustomThemes();
renderColorVars();
updateColorSectionLabel();

// ── Startup behavior ─────────────────────────────────────────────────────
/** Syncs the radio button selection with the stored preference. */
function updateStartupBehaviorBtns() {
    const current = localStorage.getItem('startup-behavior') || 'last_project';
    document.querySelectorAll('#startupBehaviorGroup input[type="radio"]').forEach(r => {
        r.checked = r.value === current;
    });
}
document.querySelectorAll('#startupBehaviorGroup input[type="radio"]').forEach(radio => {
    radio.addEventListener('change', () => {
        localStorage.setItem('startup-behavior', radio.value);
        syncThemeToServer();
    });
});
updateStartupBehaviorBtns();

// ── Autosave ──────────────────────────────────────────────────────────────
/** Syncs the autosave checkbox with the stored preference. */
function updateAutosaveToggle() {
    const enabled = localStorage.getItem('autosave') !== 'false';
    document.getElementById('autosaveToggle').checked = enabled;
}
document.getElementById('autosaveToggle').addEventListener('change', (e) => {
    localStorage.setItem('autosave', e.target.checked ? 'true' : 'false');
    syncThemeToServer();
});
updateAutosaveToggle();

// ── Undo queue size ───────────────────────────────────────────────────────
const UNDO_QUEUE_DEFAULT = 100;
/**
 * Clamps an undo queue size value to the allowed range (10–500) in multiples of 10.
 * @param {number} v - The raw value to clamp.
 * @returns {number} The clamped value.
 */
function clampUndoQueueSize(v) {
    return Math.min(500, Math.max(10, Math.round(v / 10) * 10));
}
/** Syncs the undo queue size input with the stored preference. */
function updateUndoQueueInput() {
    const size = parseInt(localStorage.getItem('undo-queue-size') || UNDO_QUEUE_DEFAULT, 10);
    document.getElementById('undoQueueSizeInput').value = clampUndoQueueSize(size);
}
document.getElementById('undoQueueSizeInput').addEventListener('change', (e) => {
    const clamped = clampUndoQueueSize(parseInt(e.target.value, 10) || UNDO_QUEUE_DEFAULT);
    e.target.value = clamped;
    localStorage.setItem('undo-queue-size', clamped);
    syncThemeToServer();
});
updateUndoQueueInput();

// ── Bundled themes (always synced from static/themes/ on load) ────────────
/**
 * Fetches bundled themes from the server and merges any new or updated entries
 * into localStorage. Called after restoreThemeFromPrefs so server prefs don't
 * overwrite newly-added bundled themes.
 * @returns {Promise<void>}
 */
async function loadBundledThemes() {
    try {
        const resp = await fetch('/api/themes');
        if (!resp.ok) return;
        const bundled = await resp.json();

        const themes  = getCustomThemes();
        let changed   = false;

        for (const t of bundled) {
            const bid = t.bundledId;
            if (!bid) continue;
            const idx = themes.findIndex(x => x.bundledId === bid);
            if (idx >= 0) {
                // Already present — just ensure the bundled flag is set
                if (!themes[idx].bundled) { themes[idx].bundled = true; changed = true; }
            } else {
                // New file on server — add it
                themes.push({
                    id:        'bundled:' + bid,
                    name:      t.name || bid,
                    base:      t.base === 'light' ? 'light' : 'dark',
                    colors:    t.colors || {},
                    bundled:   true,
                    bundledId: bid,
                });
                changed = true;
            }
        }

        if (changed) {
            saveCustomThemes(themes);
            renderCustomThemes();
            syncThemeToServer();
        }
    } catch {}
}

document.getElementById('resetAllColorsBtn').addEventListener('click', () => {
    const stored = localStorage.getItem('theme') || 'auto';
    if (BUILTIN.has(stored)) {
        localStorage.removeItem('custom-colors-' + resolvedBase());
    } else {
        const themes = getCustomThemes();
        const idx    = themes.findIndex(t => t.id === stored);
        if (idx >= 0) {
            const base = themes[idx].base;
            COLOR_VARS.forEach(v => { themes[idx].colors[v.var] = v.defaults[base]; });
            saveCustomThemes(themes);
        }
    }
    setTheme(stored);
    renderColorVars();
    syncThemeToServer();
});

// ── Theme preference sync ──────────────────────────────────────────────────
let currentPreferences = {};

/**
 * Collects current theme state from localStorage into a preferences patch.
 * @returns {{theme: string, custom_themes: Array, custom_colors_light: object, custom_colors_dark: object, startup_behavior: string}} Current preferences patch.
 */
function buildThemePrefs() {
    const patch = { theme: localStorage.getItem('theme') || 'auto', custom_themes: getCustomThemes() };
    try { patch.custom_colors_light = JSON.parse(localStorage.getItem('custom-colors-light') || '{}'); } catch { patch.custom_colors_light = {}; }
    try { patch.custom_colors_dark  = JSON.parse(localStorage.getItem('custom-colors-dark')  || '{}'); } catch { patch.custom_colors_dark  = {}; }
    patch.startup_behavior = localStorage.getItem('startup-behavior') || 'last_project';
    patch.autosave = localStorage.getItem('autosave') !== 'false';
    patch.undo_queue_size = parseInt(localStorage.getItem('undo-queue-size') || UNDO_QUEUE_DEFAULT, 10);
    return patch;
}

let _syncTimer = null;
/** Debounced (500 ms): merges current theme state into preferences and PUTs to server. */
function syncThemeToServer() {
    clearTimeout(_syncTimer);
    _syncTimer = setTimeout(async () => {
        if (!LOCAL_MODE && !authToken) return;
        currentPreferences = { ...currentPreferences, ...buildThemePrefs() };
        const headers = { 'Content-Type': 'application/json' };
        if (!LOCAL_MODE) headers['X-Auth-Token'] = authToken;
        try {
            await fetch('/api/users/me/preferences', {
                method: 'PUT',
                headers,
                body: JSON.stringify({ preferences: currentPreferences }),
            });
        } catch {}
    }, 500);
}

/**
 * Restores theme state from saved preferences into localStorage then re-applies the theme.
 * @param {{theme: string=, custom_themes: Array=, custom_colors_light: object=, custom_colors_dark: object=}} prefs - Saved user preferences object from the server.
 */
function restoreThemeFromPrefs(prefs) {
    if (!prefs) return;
    let changed = false;
    if (prefs.custom_themes !== undefined) {
        localStorage.setItem('custom-themes', JSON.stringify(prefs.custom_themes));
        changed = true;
    }
    if (prefs.custom_colors_light !== undefined) {
        if (Object.keys(prefs.custom_colors_light).length)
            localStorage.setItem('custom-colors-light', JSON.stringify(prefs.custom_colors_light));
        else
            localStorage.removeItem('custom-colors-light');
        changed = true;
    }
    if (prefs.custom_colors_dark !== undefined) {
        if (Object.keys(prefs.custom_colors_dark).length)
            localStorage.setItem('custom-colors-dark', JSON.stringify(prefs.custom_colors_dark));
        else
            localStorage.removeItem('custom-colors-dark');
        changed = true;
    }
    if (prefs.startup_behavior) {
        localStorage.setItem('startup-behavior', prefs.startup_behavior);
        updateStartupBehaviorBtns();
    }
    if (prefs.autosave !== undefined) {
        localStorage.setItem('autosave', prefs.autosave ? 'true' : 'false');
        updateAutosaveToggle();
    }
    if (prefs.undo_queue_size !== undefined) {
        localStorage.setItem('undo-queue-size', prefs.undo_queue_size);
        updateUndoQueueInput();
    }
    if (prefs.theme) {
        setTheme(prefs.theme);
        updateThemeBtns();
        renderCustomThemes();
        renderColorVars();
        updateColorSectionLabel();
    } else if (changed) {
        renderCustomThemes();
        renderColorVars();
    }
}

// ── Save status helper ─────────────────────────────────────────────────────
/**
 * Briefly displays a save status message on the given element.
 * @param {HTMLElement} el - The status element to update.
 * @param {string} text - Message to display.
 * @param {boolean} [isError=false] - Whether to style the message as an error.
 */
function showStatus(el, text, isError = false) {
    el.textContent = text;
    el.classList.toggle('save-status--error', isError);
    el.classList.add('save-status--visible');
    setTimeout(() => el.classList.remove('save-status--visible'), 2500);
}

// ── Auth & profile ─────────────────────────────────────────────────────────
let authToken = null;

// Fetch tiers immediately (public endpoint — no auth needed, server mode only)
if (!LOCAL_MODE) {
    fetch('/api/subscription-tiers')
        .then(r => r.json())
        .then(tiers => { allTiers = tiers; renderTierCards(null); })
        .catch(() => {});
}

if (LOCAL_MODE) {
    // ── Local mode: no Firebase, load profile directly ──────────────────────
    fetch('/api/users/me')
        .then(r => r.json())
        .then(user => {
            currentPreferences = user.preferences || {};
            restoreThemeFromPrefs(user.preferences);
            loadBundledThemes();
            const avatarEl = document.getElementById('profileAvatar');
            const initials = (user.display_name || 'User')
                .split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
            avatarEl.textContent = initials;
            document.getElementById('profileName').value = user.display_name || '';
        })
        .catch(() => {});
} else if (!firebaseAuth) {
    window.location.href = '/';
} else {
    onAuthStateChanged(firebaseAuth, async (fbUser) => {
        if (!fbUser) {
            window.location.href = '/';
            return;
        }
        authToken = await fbUser.getIdToken();

        let user;
        try {
            const res = await fetch('/api/users/me', { headers: { 'X-Auth-Token': authToken } });
            user = await res.json();
        } catch {
            return;
        }

        currentPreferences = user.preferences || {};
        restoreThemeFromPrefs(user.preferences);
        populateSurveyFields(user.preferences?.survey_answers);
        loadBundledThemes();

        // Avatar
        const avatarEl = document.getElementById('profileAvatar');
        if (fbUser.photoURL) {
            const img = document.createElement('img');
            img.src = fbUser.photoURL;
            img.alt = 'Profile photo';
            avatarEl.classList.add('profile-avatar--photo');
            avatarEl.appendChild(img);
        } else {
            const initials = (user.display_name || user.email || '?')
                .split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
            avatarEl.textContent = initials;
        }

        // Profile fields
        document.getElementById('profileName').value  = user.display_name || '';
        document.getElementById('profileEmail').value = user.email || '';

        const providerLabels = {
            firebase: 'Email / Password',
            google:   'Google',
            github:   'GitHub',
        };
        document.getElementById('profileProvider').textContent =
            providerLabels[user.auth_provider] || user.auth_provider || '—';

        document.getElementById('profileMemberSince').textContent = user.created_at
            ? new Date(user.created_at).toLocaleDateString(undefined, {
                year: 'numeric', month: 'long', day: 'numeric',
              })
            : '—';

        // Subscription — apply the current tier and render the tier selector
        const currentTier = user.subscription_tier || null;
        currentTierId = currentTier?.id ?? null;
        currentUsage  = user.usage || {};
        renderTierCards(currentTierId);
        applyTier(currentTier, user.preferences?.subscription_since ?? null, currentUsage);
    });
}

// ── Profile save ───────────────────────────────────────────────────────────
document.getElementById('profileSaveBtn').addEventListener('click', async () => {
    if (!LOCAL_MODE && !authToken) return;
    const name   = document.getElementById('profileName').value.trim();
    const btn    = document.getElementById('profileSaveBtn');
    const status = document.getElementById('profileSaveStatus');
    btn.disabled = true;
    try {
        const headers = { 'Content-Type': 'application/json' };
        if (!LOCAL_MODE) headers['X-Auth-Token'] = authToken;
        const res = await fetch('/api/users/me', {
            method:  'PUT',
            headers,
            body:    JSON.stringify({ display_name: name }),
        });
        if (res.ok) {
            showStatus(status, 'Saved!');
        } else {
            showStatus(status, 'Save failed.', true);
        }
    } catch {
        showStatus(status, 'Save failed.', true);
    } finally {
        btn.disabled = false;
    }
});

// ── Survey responses ───────────────────────────────────────────────────────
const _surveyFields = [
    { selectId: 'surveyProfession', otherId: 'surveyProfessionOther', key: 'profession'  },
    { selectId: 'surveyUseCase',    otherId: 'surveyUseCaseOther',    key: 'use_case'    },
    { selectId: 'surveySource',     otherId: 'surveySourceOther',     key: 'source'      },
    { selectId: 'surveyOtherTools', otherId: 'surveyOtherToolsOther', key: 'other_tools' },
];

/** Wire up "Other" free-text reveal for all survey dropdowns. */
if (document.getElementById('surveyProfession')) {
    _surveyFields.forEach(({ selectId, otherId }) => {
        const sel   = document.getElementById(selectId);
        const input = document.getElementById(otherId);
        sel.addEventListener('change', () => {
            input.style.display = sel.value === '__other__' ? '' : 'none';
            if (sel.value === '__other__') input.focus();
        });
    });
}

/**
 * Populate the survey dropdowns from saved survey_answers preferences.
 * @param {object} surveyAnswers - The preferences.survey_answers object.
 */
function populateSurveyFields(surveyAnswers) {
    if (!surveyAnswers || !document.getElementById('surveyProfession')) return;
    const knownOptions = {
        profession:  ['Journalist','Doctor / Physician','Nurse','Accountant','Lawyer','Content Editor','Researcher','Student'],
        use_case:    ['Editing interviews','Sharing transcriptions','Generating transcriptions','Creating captions & subtitles','Meeting notes','Academic research'],
        source:      ['Web search','Advertisement','Friend or colleague','Social media','Blog or article'],
        other_tools: ['Rev','Otter.ai','Descript','Sonix','Trint','None — this is my first!'],
    };
    _surveyFields.forEach(({ selectId, otherId, key }) => {
        const val   = surveyAnswers[key] || '';
        const sel   = document.getElementById(selectId);
        const input = document.getElementById(otherId);
        if (knownOptions[key].includes(val)) {
            sel.value = val;
            input.style.display = 'none';
        } else if (val) {
            sel.value = '__other__';
            input.value = val;
            input.style.display = '';
        } else {
            sel.value = '';
            input.style.display = 'none';
        }
    });
    const emailCheck = document.getElementById('surveyEmailUpdates');
    if (emailCheck) emailCheck.checked = !!surveyAnswers.email_updates;
}

if (document.getElementById('surveySaveBtn')) {
    document.getElementById('surveySaveBtn').addEventListener('click', async () => {
        if (!authToken) return;
        const btn    = document.getElementById('surveySaveBtn');
        const status = document.getElementById('surveySaveStatus');
        btn.disabled = true;

        const surveyAnswers = {};
        _surveyFields.forEach(({ selectId, otherId, key }) => {
            const sel = document.getElementById(selectId);
            if (sel.value === '__other__') {
                const v = document.getElementById(otherId).value.trim();
                if (v) surveyAnswers[key] = v;
            } else if (sel.value) {
                surveyAnswers[key] = sel.value;
            }
        });
        surveyAnswers.email_updates = document.getElementById('surveyEmailUpdates').checked;

        currentPreferences = { ...currentPreferences, survey_answers: surveyAnswers };
        try {
            const res = await fetch('/api/users/me/preferences', {
                method:  'PUT',
                headers: { 'Content-Type': 'application/json', 'X-Auth-Token': authToken },
                body:    JSON.stringify({ preferences: currentPreferences }),
            });
            if (res.ok) {
                showStatus(status, 'Saved!');
            } else {
                showStatus(status, 'Save failed.', true);
            }
        } catch {
            showStatus(status, 'Save failed.', true);
        } finally {
            btn.disabled = false;
        }
    });
}

// ── Subscription tiers ─────────────────────────────────────────────────────
let allTiers      = [];
let billingMode   = 'monthly';
let currentTierId = null;
let currentUsage  = {};

/**
 * Formats a minute value for display, returning 'Unlimited' for null.
 * @param {number|null} mins - Minutes to format.
 * @returns {string}
 */
function fmtMins(mins) {
    if (mins == null) return 'Unlimited';
    return mins >= 60 ? `${mins / 60} hr` : `${mins} min`;
}

/**
 * Formats an hour value for display, returning 'Unlimited' for null.
 * @param {number|null} hrs - Hours to format.
 * @returns {string}
 */
function fmtHrs(hrs)  { return hrs  == null ? 'Unlimited' : `${hrs} hr`; }
/**
 * Formats a gigabyte value for display, returning 'Unlimited' for null.
 * @param {number|null} gb - Gigabytes to format.
 * @returns {string}
 */
function fmtGb(gb)    { return gb   == null ? 'Unlimited' : `${gb} GB`;  }

/**
 * Populates the subscription info panel with the given tier's details.
 * @param {object|null} tier - The active subscription tier object, or null.
 * @param {string|null} [subscriptionSince=null] - ISO date string of when the subscription started.
 * @param {object} [usage={}] - Current usage counters (e.g. transcription_mins, storage_bytes).
 */
function applyTier(tier, subscriptionSince = null, usage = {}) {
    const f = tier?.features || {};
    document.getElementById('planName').textContent    = f.display_name    || '—';
    document.getElementById('planTagline').textContent = tier?.description  || '';
    document.getElementById('planBadge').textContent   = f.display_name    || '—';

    // Transcription usage
    const usedMins  = parseFloat(usage.transcription_mins || 0);
    const limitHrs  = f.transcription_hrs_month ?? null;
    const usedHrsDisplay = usedMins < 60
        ? `${Math.round(usedMins)} min`
        : `${(usedMins / 60).toFixed(1)} hr`;
    document.getElementById('usageTranscription').textContent =
        `${usedHrsDisplay} / ${fmtHrs(limitHrs)}`;
    const transcriptionPct = limitHrs != null
        ? Math.min(100, (usedMins / (limitHrs * 60)) * 100) : 0;
    document.getElementById('usageTranscriptionBar').style.width = `${transcriptionPct}%`;

    // Storage usage
    const usedBytes   = parseFloat(usage.storage_bytes || 0);
    const limitGb     = f.storage_gb ?? null;
    const usedGb      = usedBytes / (1024 ** 3);
    const usedGbDisplay = usedGb < 1
        ? `${(usedBytes / (1024 ** 2)).toFixed(0)} MB`
        : `${usedGb.toFixed(2)} GB`;
    document.getElementById('usageStorage').textContent =
        `${usedGbDisplay} / ${fmtGb(limitGb)}`;
    const storagePct = limitGb != null
        ? Math.min(100, (usedGb / limitGb) * 100) : 0;
    document.getElementById('usageStorageBar').style.width = `${storagePct}%`;

    document.getElementById('allowanceAudioLen').textContent        = fmtMins(f.max_audio_mins);
    document.getElementById('allowanceTranscriptionLen').textContent = fmtMins(f.max_transcription_mins);
    document.getElementById('allowanceWhisperModels').textContent   = (f.whisper_models || []).join(', ') || '—';
    document.getElementById('cancelPlanBtn').style.display          = (f.price_usd_month > 0) ? '' : 'none';

    const isPaid = f.price_usd_month > 0;
    const periodLabels = { monthly: 'Monthly', yearly: 'Yearly' };
    document.getElementById('billingPeriod').textContent =
        (isPaid && tier) ? (periodLabels[tier.billing_period] || tier.billing_period) : '—';

    if (isPaid && subscriptionSince && tier) {
        const next = new Date(subscriptionSince);
        if (tier.billing_period === 'yearly') next.setFullYear(next.getFullYear() + 1);
        else next.setMonth(next.getMonth() + 1);
        document.getElementById('billingNextPayment').textContent =
            next.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' });
    } else {
        document.getElementById('billingNextPayment').textContent = '—';
    }

    updateTierCards(tier?.id);
}

/**
 * Builds and renders the tier selection cards into the tier grid.
 * @param {string|null} activeTierId - The ID of the currently active tier, or null.
 */
function renderTierCards(activeTierId) {
    const grid = document.getElementById('tierGrid');
    grid.innerHTML = '';

    const visibleTiers = allTiers.filter(t => t.billing_period === billingMode);

    visibleTiers.forEach((tier, i) => {
        const f          = tier.features || {};
        const isCurrent  = tier.id === activeTierId;
        const isFeatured = i === 1;

        const features = [
            `${fmtMins(f.max_audio_mins)} max audio length`,
            `${fmtMins(f.max_transcription_mins)} max transcription`,
            `${fmtHrs(f.transcription_hrs_month)} transcription / month`,
            `${fmtGb(f.storage_gb)} storage`,
            `Whisper: ${(f.whisper_models || []).join(', ')}`,
        ];

        let priceHtml;
        if (f.price_usd_month == null) {
            priceHtml = `<span class="tier-price-amount">Custom</span>`;
        } else if (f.price_usd_month === 0) {
            priceHtml = `<span class="tier-price-amount">$0</span><span class="tier-price-period">/mo</span>`;
        } else if (tier.billing_period === 'yearly') {
            const saving = (f.price_usd_month_base - f.price_usd_month).toFixed(2);
            priceHtml = `
                <span class="tier-price-amount">$${f.price_usd_year.toFixed(2)}</span><span class="tier-price-period">/yr</span>
                <div class="tier-price-breakdown">$${f.price_usd_month.toFixed(2)}/mo &middot; saves $${saving}/mo</div>`;
        } else {
            priceHtml = `<span class="tier-price-amount">$${f.price_usd_month}</span><span class="tier-price-period">/mo</span>`;
        }

        const card = document.createElement('div');
        card.className = ['tier-card', isFeatured && 'tier-card--featured', isCurrent && 'tier-card--current']
            .filter(Boolean).join(' ');
        card.innerHTML = `
            ${isFeatured ? '<div class="tier-card-highlight">Most Popular</div>' : ''}
            <div class="tier-name">${f.display_name || tier.name}</div>
            <div class="tier-price">${priceHtml}</div>
            <ul class="tier-features">${features.map(l => `<li>${l}</li>`).join('')}</ul>
            <button class="btn tier-select-btn" data-tier-id="${tier.id}" ${isCurrent ? 'disabled' : ''}>
                ${isCurrent ? 'Current Plan' : 'Select'}
            </button>`;

        card.querySelector('.tier-select-btn').addEventListener('click', () => selectTier(tier));
        grid.appendChild(card);
    });
}

/**
 * Updates the active/disabled state of existing tier cards without re-rendering.
 * @param {string|null} currentTierId - The ID of the currently active tier.
 */
function updateTierCards(currentTierId) {
    document.querySelectorAll('.tier-card').forEach(card => {
        const btn     = card.querySelector('.tier-select-btn');
        const tierId  = btn?.dataset.tierId;
        const current = tierId === currentTierId;
        card.classList.toggle('tier-card--current', current);
        if (btn) { btn.textContent = current ? 'Current Plan' : 'Select'; btn.disabled = current; }
    });
}

/**
 * Sends a subscription update request for the selected tier.
 * @param {object} tier - The tier object to subscribe to.
 */
async function selectTier(tier) {
    if (!authToken) return;
    const btn = document.querySelector(`.tier-select-btn[data-tier-id="${tier.id}"]`);
    if (btn) btn.disabled = true;
    try {
        const res = await fetch('/api/users/me/subscription', {
            method:  'PUT',
            headers: { 'X-Auth-Token': authToken, 'Content-Type': 'application/json' },
            body:    JSON.stringify({ subscription_id: tier.id }),
        });
        if (res.ok) {
            currentTierId = tier.id;
            applyTier(tier, new Date().toISOString(), currentUsage);
            closeTierModal();
        }
    } finally {
        if (btn) btn.disabled = false;
    }
}

// ── Tier modal ─────────────────────────────────────────────────────────────
const tierModalOverlay = document.getElementById('tierModalOverlay');

/** Opens the tier selection modal. */
function openTierModal()  { tierModalOverlay.style.display = 'flex'; }
/** Closes the tier selection modal. */
function closeTierModal() { tierModalOverlay.style.display = 'none'; }

document.querySelectorAll('.billing-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        billingMode = btn.dataset.billing;
        document.querySelectorAll('.billing-btn').forEach(b => b.classList.toggle('active', b === btn));
        renderTierCards(currentTierId);
    });
});

document.getElementById('upgradePlanBtn').addEventListener('click', openTierModal);
document.getElementById('tierModalClose').addEventListener('click', closeTierModal);
let _tierModalMouseDown = false;
tierModalOverlay.addEventListener('mousedown', (e) => { _tierModalMouseDown = e.target === tierModalOverlay; });
tierModalOverlay.addEventListener('click', e => { if (e.target === tierModalOverlay && _tierModalMouseDown) closeTierModal(); });
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeTierModal(); });

// ── Delete account ─────────────────────────────────────────────────────────
document.getElementById('deleteAccountBtn')?.addEventListener('click', async () => {
    if (!confirm(
        'This will permanently delete your account and all associated data.\n\n' +
        'This action cannot be undone. Are you sure?'
    )) return;
    if (!authToken) return;
    try {
        await fetch('/api/users/me', {
            method: 'DELETE', headers: { 'X-Auth-Token': authToken },
        });
    } finally {
        if (firebaseAuth) await signOut(firebaseAuth);
        window.location.href = '/';
    }
});