/* ============================================================
   PPphp Designer — Theme runtime
   Client mirror of core/Theme/ThemeCompiler: compiles a theme's
   token map into scoped `--pp-*` CSS variables and applies it to
   the canvas (with a per-theme dark variant toggle).
   ============================================================ */
(function () {
    'use strict';
    var PPD = window.PPD = window.PPD || {};
    var boot = window.PpDesignerBootstrap || {};

    var VARMAP = {
        bg: '--pp-bg', surface: '--pp-surface', surfaceSoft: '--pp-surface-soft',
        text: '--pp-text', muted: '--pp-muted', border: '--pp-border',
        primary: '--pp-primary', primarySoft: '--pp-primary-soft', primaryText: '--pp-primary-text',
        success: '--pp-success', warning: '--pp-warning', danger: '--pp-danger',
        radius: '--pp-radius', shadow: '--pp-shadow', font: '--pp-font', mono: '--pp-mono'
    };
    // also drive the designer's existing --ppd-* component tokens (no per-component CSS changes).
    // Crucially, form-input tokens (--pp-input-*) are emitted from the design theme's
    // surface/text/border so canvas inputs NEVER inherit the console app-theme's
    // --pp-input-bg (which caused dark input boxes on a light canvas). Mirrors the
    // generated app, where .pp-input falls back to --pp-surface.
    var ALIAS = {
        bg: ['--ppd-bg'], surface: ['--ppd-surface', '--pp-input-bg'], surfaceSoft: ['--ppd-soft'],
        text: ['--ppd-text', '--pp-input-text'], muted: ['--ppd-muted'], border: ['--ppd-border', '--ppd-border-soft', '--pp-input-border'],
        primary: ['--ppd-primary'], primarySoft: ['--ppd-primary-soft', '--ppd-primary-border'],
        danger: ['--ppd-danger'], success: ['--ppd-ok'], shadow: ['--ppd-shadow']
    };

    function list() { return Array.isArray(boot.themes) ? boot.themes : []; }
    function defaultId() { return boot.themeDefault || 'default'; }
    function get(id) {
        var all = list();
        return all.filter(function (t) { return t.id === id; })[0] || all.filter(function (t) { return t.id === defaultId(); })[0] || all[0] || null;
    }

    function kebab(k) { return String(k).replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); }
    function sanitize(v) { return String(v == null ? '' : v).replace(/[{};<>]|@import|expression\(/g, '').trim(); }

    function block(selector, tokens) {
        var lines = [];
        Object.keys(tokens || {}).forEach(function (k) {
            var v = sanitize(tokens[k]);
            if (!v) return;
            lines.push('  ' + (VARMAP[k] || ('--pp-' + kebab(k))) + ': ' + v + ';');
            (ALIAS[k] || []).forEach(function (a) { lines.push('  ' + a + ': ' + v + ';'); });
            var m = k === 'radius' && /^(\d+(?:\.\d+)?)px$/.exec(v);
            if (m) {
                var n = parseFloat(m[1]);
                lines.push('  --pp-radius-sm: ' + Math.round(n * 0.6 * 10) / 10 + 'px;');
                lines.push('  --pp-radius-lg: ' + Math.round(n * 1.6 * 10) / 10 + 'px;');
            }
        });
        return lines.length ? selector + ' {\n' + lines.join('\n') + '\n}' : '';
    }

    // Baseline dark palette merged UNDER a theme's own dark tokens (mirror of
    // ThemeCompiler::DEFAULT_DARK) so partial/empty dark maps still render a
    // complete dark mode. Brand/status tokens keep the theme's light values.
    var DEFAULT_DARK = {
        bg: '#0f172a', surface: '#1e293b', surfaceSoft: '#26334a',
        text: '#e2e8f0', muted: '#94a3b8', border: '#334155',
        primarySoft: 'rgba(96,165,250,0.16)', shadow: '0 12px 32px rgba(0,0,0,0.45)'
    };
    function darkTokens(theme) {
        var out = {}, k, t = (theme && theme.dark) || {};
        for (k in DEFAULT_DARK) out[k] = DEFAULT_DARK[k];
        for (k in t) { if (String(t[k] == null ? '' : t[k]).trim() !== '') out[k] = t[k]; }
        return out;
    }

    // compile a theme object to CSS scoped under `scope`
    function compile(theme, scope) {
        if (!theme) return '';
        scope = scope || ':root';
        var light = block(scope, theme.tokens || {});
        // Always emit dark: theme dark tokens over the baseline, plus grid
        // surfaces rebound to the dark surface (Grid tab colours are
        // light-mode values — without this, tables keep light rows).
        var dark = block(scope + '.is-dark', darkTokens(theme))
            + '\n' + scope + '.is-dark {\n  --pp-grid-panel: var(--pp-surface);\n  --pp-grid-bg: var(--pp-surface);\n}';
        return (light + '\n' + dark).trim();
    }

    function styleEl() {
        var el = document.getElementById('ppdThemeStyle');
        if (!el) { el = document.createElement('style'); el.id = 'ppdThemeStyle'; document.head.appendChild(el); }
        return el;
    }

    // apply a theme (by id) to the canvas, honouring the dark-preview flag
    function apply(id, dark) {
        var theme = get(id);
        var canvas = document.getElementById('ppdCanvas');
        styleEl().textContent = compile(theme, '.ppd-canvas');
        if (canvas) {
            canvas.classList.toggle('is-dark', !!dark);
            canvas.setAttribute('data-theme-id', theme ? theme.id : '');
        }
    }

    PPD.theme = { list: list, get: get, defaultId: defaultId, compile: compile, apply: apply, VARMAP: VARMAP };
})();
