// LPCharts — tiny, dependency-free SVG charts for the Admin → System page.
// No external libraries or CDN calls (LibrePortal ships no third-party frontend
// assets). Every renderer returns an SVG string and colours itself from the
// active theme's CSS variables, so charts re-theme for free. Stroke widths use
// vector-effect:non-scaling-stroke so lines stay crisp when the SVG is stretched
// to its container width.
const LPCharts = (() => {
    let _uid = 0;
    const uid = (p) => `${p}-${(++_uid)}`;

    // name -> { line: "var(--name)", fill: "rgba(var(--name-rgb), a)" }
    const palette = (name = 'accent') => ({
        line: `var(--${name})`,
        rgb: `var(--${name}-rgb)`
    });

    // Map a series of numbers to SVG path data within a [0,W]x[0,H] box.
    // Returns { line, area } path strings. `max`/`min` fix the y-range.
    function paths(values, W, H, pad, min, max) {
        const n = values.length;
        if (n === 0) return { line: '', area: '' };
        const span = (max - min) || 1;
        const innerH = H - pad * 2;
        const stepX = n > 1 ? (W / (n - 1)) : 0;
        const pts = values.map((v, i) => {
            const x = n > 1 ? i * stepX : W / 2;
            const y = pad + innerH * (1 - (Math.max(min, Math.min(max, v)) - min) / span);
            return [x, y];
        });
        const line = pts.map((p, i) => `${i ? 'L' : 'M'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ');
        const area = `${line} L${pts[n - 1][0].toFixed(1)},${H} L${pts[0][0].toFixed(1)},${H} Z`;
        return { line, area };
    }

    // Single filled area chart. values: number[]. opts: { color, height, max,
    // min, unit, fmt }. Returns an <svg> that fills its container width.
    function areaChart(values, opts = {}) {
        const W = 300, H = opts.height || 90, pad = 6;
        const color = palette(opts.color || 'accent');
        if (!values || values.length === 0) {
            return `<div class="lp-chart-empty">No data yet</div>`;
        }
        const dataMax = Math.max(...values);
        const dataMin = Math.min(...values);
        const max = (opts.max !== undefined) ? opts.max : (dataMax <= 0 ? 1 : dataMax * 1.15);
        const min = (opts.min !== undefined) ? opts.min : Math.min(0, dataMin);
        const { line, area } = paths(values, W, H, pad, min, max);
        const id = uid('grad');
        const last = values[values.length - 1];
        const fmt = opts.fmt || ((v) => v);
        return `
        <svg class="lp-chart" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none" role="img">
            <defs>
                <linearGradient id="${id}" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%"  stop-color="rgba(${getRGB(color.rgb)}, 0.35)"/>
                    <stop offset="100%" stop-color="rgba(${getRGB(color.rgb)}, 0.02)"/>
                </linearGradient>
            </defs>
            <path d="${area}" fill="url(#${id})" stroke="none"/>
            <path d="${line}" fill="none" stroke="${color.line}" stroke-width="2"
                  vector-effect="non-scaling-stroke" stroke-linejoin="round" stroke-linecap="round"/>
        </svg>
        <div class="lp-chart-last" style="color:${color.line}">${fmt(last)}</div>`;
    }

    // Multiple overlaid lines on a shared y-range. series: [{values, color, label}].
    function multiLine(series, opts = {}) {
        const W = 300, H = opts.height || 90, pad = 6;
        const all = series.flatMap(s => s.values || []);
        if (all.length === 0) return `<div class="lp-chart-empty">No data yet</div>`;
        const max = (opts.max !== undefined) ? opts.max : (Math.max(...all) * 1.15 || 1);
        const min = (opts.min !== undefined) ? opts.min : 0;
        const lines = series.map(s => {
            const c = palette(s.color || 'accent');
            const { line } = paths(s.values || [], W, H, pad, min, max);
            return `<path d="${line}" fill="none" stroke="${c.line}" stroke-width="2"
                vector-effect="non-scaling-stroke" stroke-linejoin="round" stroke-linecap="round"/>`;
        }).join('');
        return `<svg class="lp-chart" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none" role="img">${lines}</svg>`;
    }

    // Inline mini line (no axes/fill). For per-app rows.
    function sparkline(values, opts = {}) {
        const W = 100, H = opts.height || 24, pad = 2;
        const color = palette(opts.color || 'accent');
        if (!values || values.length < 2) return `<svg class="lp-spark" viewBox="0 0 ${W} ${H}"></svg>`;
        const max = Math.max(...values) || 1;
        const { line } = paths(values, W, H, pad, 0, max * 1.1);
        return `<svg class="lp-spark" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none">
            <path d="${line}" fill="none" stroke="${color.line}" stroke-width="1.5"
                vector-effect="non-scaling-stroke" stroke-linejoin="round"/></svg>`;
    }

    // Circular ring gauge. value 0..max. opts: { label, color, sublabel, max }.
    function gauge(value, opts = {}) {
        const max = opts.max || 100;
        const pct = Math.max(0, Math.min(1, (value || 0) / max));
        const color = palette(opts.color || pickColor(pct));
        const r = 52, C = 2 * Math.PI * r, off = C * (1 - pct);
        return `
        <div class="lp-gauge">
            <svg viewBox="0 0 120 120" class="lp-gauge-svg">
                <circle cx="60" cy="60" r="${r}" fill="none" stroke="rgba(var(--text-rgb),0.10)" stroke-width="10"/>
                <circle cx="60" cy="60" r="${r}" fill="none" stroke="${color.line}" stroke-width="10"
                    stroke-linecap="round" stroke-dasharray="${C.toFixed(1)}" stroke-dashoffset="${off.toFixed(1)}"
                    transform="rotate(-90 60 60)" style="transition:stroke-dashoffset .5s ease"/>
            </svg>
            <div class="lp-gauge-center">
                <div class="lp-gauge-value">${opts.display !== undefined ? opts.display : Math.round(value)}<span>${opts.suffix || '%'}</span></div>
                ${opts.sublabel ? `<div class="lp-gauge-sub">${opts.sublabel}</div>` : ''}
            </div>
            <div class="lp-gauge-label">${opts.label || ''}</div>
        </div>`;
    }

    // Horizontal percentage bar.
    function bar(pct, opts = {}) {
        const p = Math.max(0, Math.min(100, pct || 0));
        const color = palette(opts.color || pickColor(p / 100));
        return `<div class="lp-bar"><span class="lp-bar-fill" style="width:${p}%;background:${color.line}"></span></div>`;
    }

    // Green < 70% < amber < 90% < red — the universal "headroom" cue.
    function pickColor(frac) {
        if (frac >= 0.9) return 'status-danger';
        if (frac >= 0.7) return 'status-warning';
        return 'status-success';
    }

    // Resolve "var(--x-rgb)" to its literal triplet so it can sit inside a
    // gradient stop's rgba() (SVG gradients don't inherit CSS custom props
    // reliably across browsers when nested in rgba()).
    function getRGB(varExpr) {
        const name = varExpr.match(/--[a-z0-9-]+/i);
        if (!name) return '0,212,255';
        const v = getComputedStyle(document.documentElement).getPropertyValue(name[0]).trim();
        return v || '0,212,255';
    }

    return { areaChart, multiLine, sparkline, gauge, bar, pickColor };
})();

window.LPCharts = LPCharts;
