/**
 * Admin > Monitoring > Health
 *
 * Polls GET /api/v1/admin/metrics and displays runtime, DB, cluster,
 * and extension metrics. Cluster panel only shows on PG multi-node.
 */
const { html } = window;
const { useState, useEffect, useRef, useCallback } = hooks;

// ── Formatting helpers ──────────────────────────

function formatBytes(b) {
    if (b == null) return '\u2014';
    if (b < 1024) return b + ' B';
    if (b < 1024 * 1024) return (b / 1024).toFixed(1) + ' KB';
    if (b < 1024 * 1024 * 1024) return (b / (1024 * 1024)).toFixed(1) + ' MB';
    return (b / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
}

function formatDuration(sec) {
    if (sec == null) return '\u2014';
    if (sec < 60) return Math.floor(sec) + 's';
    if (sec < 3600) return Math.floor(sec / 60) + 'm ' + Math.floor(sec % 60) + 's';
    const h = Math.floor(sec / 3600);
    const m = Math.floor((sec % 3600) / 60);
    if (h < 24) return h + 'h ' + m + 'm';
    const d = Math.floor(h / 24);
    return d + 'd ' + (h % 24) + 'h';
}

function formatMs(ms) {
    if (ms == null) return '\u2014';
    if (ms < 1) return '<1 ms';
    if (ms < 1000) return ms.toFixed(1) + ' ms';
    return (ms / 1000).toFixed(2) + ' s';
}

function formatPct(v) {
    if (v == null) return '\u2014';
    return v.toFixed(2) + '%';
}

function formatNum(v) {
    if (v == null) return '\u2014';
    if (typeof v === 'number') return v.toLocaleString();
    return String(v);
}

// ── Stat card ───────────────────────────────────

function Stat({ label, value, hint }) {
    return html`
        <div style="padding:8px 12px;background:var(--bg-secondary, #f5f5f5);border-radius:6px;min-width:120px;" title=${hint || ''}>
            <div style="font-size:11px;color:var(--text-muted, #888);margin-bottom:2px;">
                ${label}${hint ? html`<span style="margin-left:3px;cursor:help;opacity:0.5;" title=${hint}>\u24D8</span>` : ''}
            </div>
            <div style="font-size:16px;font-weight:600;font-variant-numeric:tabular-nums;">${value}</div>
        </div>
    `;
}

// ── Panels ──────────────────────────────────────

function PanelHeader({ title }) {
    return html`<h3 style="margin:0 0 12px;font-size:14px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted, #888);">${title}</h3>`;
}

function RuntimePanel({ m }) {
    return html`
        <div style="margin-bottom:24px;">
            <${PanelHeader} title="Runtime" />
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px;">
                <${Stat} label="Uptime"           value=${formatDuration(m.uptime_sec)} />
                <${Stat} label="Goroutines"        value=${formatNum(m.goroutines)} />
                <${Stat} label="Heap Alloc"        value=${formatBytes(m.heap_alloc)} />
                <${Stat} label="Heap Sys"          value=${formatBytes(m.heap_sys)} />
                <${Stat} label="Stack In Use"      value=${formatBytes(m.stack_in_use)} />
                <${Stat} label="GC Cycles"         value=${formatNum(m.gc_cycles)} />
                <${Stat} label="Last GC Pause"     value=${formatMs((m.gc_pause_ns || 0) / 1e6)} />
                <${Stat} label="GC CPU"            value=${formatPct(m.gc_cpu_pct)} />
                <${Stat} label="WS Clients"        value=${formatNum(m.ws_clients)} />
                <${Stat} label="Extensions"        value=${formatNum(m.extensions_loaded)} />
                <${Stat} label="Open FDs"          value=${m.open_fds >= 0 ? formatNum(m.open_fds) : '\u2014'} />
            </div>
        </div>
    `;
}

function DBPanel({ m }) {
    return html`
        <div style="margin-bottom:24px;">
            <${PanelHeader} title="Database" />
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px;">
                <${Stat} label="Latency"        value=${formatMs(m.latency_ms)} />
                <${Stat} label="Pool Active"    value=${formatNum(m.pool_active)} />
                <${Stat} label="Pool Idle"      value=${formatNum(m.pool_idle)} />
                <${Stat} label="Pool Max"       value=${formatNum(m.pool_max)} />
                <${Stat} label="Wait Count"     value=${formatNum(m.wait_count)} />
                <${Stat} label="Wait Duration"  value=${formatMs(m.wait_duration_ms)} />
                ${m.dead_tuples != null && html`<${Stat} label="Dead Tuples" value=${formatNum(m.dead_tuples)} />`}
                ${m.active_backends != null && html`<${Stat} label="Active Backends" value=${formatNum(m.active_backends)} />`}
            </div>
        </div>
    `;
}

function ClusterPanel({ cluster }) {
    return html`
        <div style="margin-bottom:24px;">
            <${PanelHeader} title=${'Cluster \u2014 ' + cluster.size + ' node' + (cluster.size !== 1 ? 's' : '')} />
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;">
                ${cluster.nodes.map(n => html`<${NodeCard} key=${n.node_id} node=${n} />`)}
            </div>
        </div>
    `;
}

function NodeCard({ node }) {
    const stats = node.stats || {};
    const ageLabel = node.heartbeat_age_ms < 15000
        ? Math.floor(node.heartbeat_age_ms / 1000) + 's ago'
        : Math.floor(node.heartbeat_age_ms / 60000) + 'm ago';
    const ageColor = node.heartbeat_age_ms < 15000 ? 'var(--accent, #2a6)' : 'var(--warning, #c80)';

    return html`
        <div style="border:1px solid var(--border, #ddd);border-radius:8px;padding:12px;background:var(--bg-primary, #fff);overflow:hidden;">
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;gap:8px;">
                <strong style="font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;" title=${node.node_id}>${node.node_id}</strong>
                <span style="font-size:11px;color:${ageColor};font-weight:600;flex-shrink:0;">${ageLabel}</span>
            </div>
            ${node.endpoint && html`<div style="font-size:11px;color:var(--text-muted, #888);margin-bottom:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${node.endpoint}</div>`}
            <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px;overflow:hidden;">
                <${Stat} label="Uptime"      value=${formatDuration(stats.uptime_sec)} />
                <${Stat} label="WS Clients"  value=${formatNum(stats.ws_clients)} />
                <${Stat} label="Goroutines"  value=${formatNum(stats.goroutines)} />
                <${Stat} label="Heap"        value=${formatBytes(stats.heap_alloc)} />
                <${Stat} label="GC Pause"    value=${formatMs((stats.gc_pause_ns || 0) / 1e6)} />
                <${Stat} label="GC Cycles"   value=${formatNum(stats.gc_cycles)} />
            </div>
        </div>
    `;
}

function ExtensionPanel({ m }) {
    return html`
        <div style="margin-bottom:24px;">
            <${PanelHeader} title="Extensions" />
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px;">
                <${Stat} label="Starlark Execs"    value=${formatNum(m.starlark_exec_total)} />
                <${Stat} label="Starlark Errors"   value=${formatNum(m.starlark_errors_total)} />
                <${Stat} label="Avg Duration"       value=${formatMs(m.starlark_avg_duration_ms)} />
                <${Stat} label="Trigger Fires"     value=${formatNum(m.trigger_fires_total)} />
                <${Stat} label="Events Published"  value=${formatNum(m.event_bus_published)} hint="Total events emitted by the event bus. Not all events have active subscribers \u2014 published > delivered is normal (e.g. startup events fire before any clients connect)." />
                <${Stat} label="Events Delivered"  value=${formatNum(m.event_bus_delivered)} hint="Events that matched at least one subscriber and were dispatched. Lower than published when events fire with no listeners (e.g. during startup)." />
            </div>
        </div>
    `;
}

// ── Main component ──────────────────────────────

export default function HealthSection() {
    const [data, setData] = useState(null);
    const [error, setError] = useState(null);
    const [interval, setInterval_] = useState(10);
    const timerRef = useRef(null);

    const load = useCallback(async () => {
        try {
            const resp = await sw.api.admin.metrics();
            setData(resp);
            setError(null);
        } catch (e) {
            setError(e.message);
        }
    }, []);

    useEffect(() => {
        load();
        timerRef.current = setInterval(load, interval * 1000);
        return () => clearInterval(timerRef.current);
    }, [load, interval]);

    if (error && !data) {
        return html`<div class="settings-placeholder" style="color:var(--error,red);">Failed to load metrics: ${error}</div>`;
    }
    if (!data) {
        return html`<div class="settings-placeholder">Loading metrics\u2026</div>`;
    }

    return html`
        <div>
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;gap:8px;">
                ${data.node_id && html`<span style="font-size:11px;color:var(--text-muted, #888);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;" title=${data.node_id}>Viewing from <strong style="color:var(--text-primary, #ccc);">${data.node_id}</strong></span>`}
                <span style="font-size:11px;color:var(--text-muted, #888);flex-shrink:0;">Refresh</span>
                <select style="font-size:12px;padding:2px 6px;" value=${interval} onChange=${e => setInterval_(+e.target.value)}>
                    <option value="5">5s</option>
                    <option value="10">10s</option>
                    <option value="30">30s</option>
                    <option value="60">60s</option>
                </select>
            </div>
            <${RuntimePanel} m=${data.runtime} />
            <${DBPanel} m=${data.db} />
            ${data.cluster && html`<${ClusterPanel} cluster=${data.cluster} />`}
            <${ExtensionPanel} m=${data.extensions} />
        </div>
    `;
}
