/**
 * context-log.js — Standalone decision-spine context and decision browser.
 */

import { S } from '../state.js';
import { api, bustCache } from '../api.js';

const $ = id => document.getElementById(id);
const esc = s => s == null ? '' : String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

function fmtTs(ts) {
    if (!ts) return '—';
    try { return new Date(ts).toLocaleString(); } catch { return '—'; }
}

function runIdOf(run) {
    return run?.runId || run?.id || '';
}

function spineUrl(runId) {
    return '/api/runs/' + encodeURIComponent(runId) + '/decision-spine';
}

function learningUrl(runId) {
    return '/api/runs/' + encodeURIComponent(runId) + '/learning';
}

function chips(items, limit = 12) {
    const values = (items || []).filter(Boolean).slice(0, limit);
    if (!values.length) return '<span class="context-log-empty">—</span>';
    return values.map(value => {
        const raw = String(value);
        const label = raw.replace(/^.*\//, '');
        const target = /memory|episodic|semantic|working/i.test(raw)
            ? 'memory'
            : /\.(ts|tsx|js|jsx|css|html|md|json)$/i.test(raw)
                ? 'repo'
                : '';
        return `<button class="chip chip-muted context-link-chip" ${target ? `data-context-target="${target}" data-context-value="${esc(raw)}"` : ''}>${esc(label)}</button>`;
    }).join('');
}

function contextRouterOf(spine) {
    const plan = spine?.artifacts?.selectionPlan || {};
    return spine?.summary?.contextRouter || plan.contextRouter || {};
}

function selectedOf(spine) {
    const plan = spine?.artifacts?.selectionPlan || {};
    return spine?.summary?.selected || plan.selected || {};
}

function changedFilesOf(spine) {
    const router = contextRouterOf(spine);
    const selected = selectedOf(spine);
    return router.changedFiles || selected.changedFiles || [];
}

function summaryCard(spine) {
    const artifacts = spine?.artifacts || {};
    const brief = artifacts.requestBrief || {};
    const plan = artifacts.selectionPlan || {};
    const model = spine?.summary?.modelRoute || plan.modelRoute || brief.modelPolicy || {};
    const selected = selectedOf(spine);
    const router = contextRouterOf(spine);
    const changedFiles = changedFilesOf(spine);
    const providerChoices = Array.isArray(model.providerChoices) ? model.providerChoices : [];
    const modelChoices = providerChoices.length
        ? providerChoices.map(choice => `${choice.selected ? 'active ' : ''}${choice.client}:${choice.model}`)
        : [model.selectedModel || model.workerTier || '—'];
    return `<div class="context-log-summary">
        <div class="context-log-kpi"><span>Intent</span><strong>${esc(brief.intent || plan.intent || spine?.summary?.intent || '—')}</strong></div>
        <div class="context-log-kpi"><span>Risk</span><strong>${esc(brief.risk || '—')}</strong></div>
        <div class="context-log-kpi"><span>Model</span><strong>${esc(model.workerTier || '—')}${model.reviewerTier ? ` / ${esc(model.reviewerTier)}` : ''}</strong></div>
        <div class="context-log-kpi"><span>Route</span><strong>${esc(router.status || '—')}</strong></div>
        <div class="context-log-kpi"><span>Workers</span><strong>${esc(router.workersExecuted ?? 0)}/${esc(router.workersPlanned ?? selected.workers?.length ?? 0)}</strong></div>
        <div class="context-log-kpi"><span>Actions</span><strong>${esc(router.actionsBound ?? selected.actions?.length ?? 0)}</strong></div>
        <div class="context-log-kpi"><span>Changed</span><strong>${esc(changedFiles.length)}</strong></div>
        <div class="context-log-selection">
            <div><span>Crew</span>${chips(selected.crews || router.selectedCrew, 12)}</div>
            <div><span>Specialists</span>${chips(selected.specialists || router.specialists, 18)}</div>
            <div><span>Files</span>${chips(selected.files, 18)}</div>
            <div><span>Skills</span>${chips(selected.skills, 18)}</div>
            <div><span>Changed files</span>${chips(changedFiles, 18)}</div>
            <div><span>Model lanes</span>${chips(modelChoices, 6)}</div>
            <div><span>Decisions</span>${chips([`${spine?.summary?.decisions ?? artifacts.decisionLog?.length ?? 0} logged`, `${spine?.summary?.contextEvents ?? artifacts.contextLog?.length ?? 0} context events`], 4)}</div>
        </div>
    </div>`;
}

function routerPanel(spine) {
    const selected = selectedOf(spine);
    const router = contextRouterOf(spine);
    const changedFiles = changedFilesOf(spine);
    const status = router.status || (changedFiles.length ? 'changed' : 'planned');
    return `<section class="context-router-card context-router-${esc(status)}">
        <div class="context-router-head">
            <div>
                <span>Nexus Prime Context Router</span>
                <strong>${esc(status)}</strong>
            </div>
            <div class="context-router-metrics">
                <span>${esc(router.workersExecuted ?? 0)}/${esc(router.workersPlanned ?? selected.workers?.length ?? 0)} workers</span>
                <span>${esc(router.actionsBound ?? selected.actions?.length ?? 0)} actions</span>
                <span>${esc(router.workersVerified ?? 0)} verified</span>
                <span>${esc(changedFiles.length)} changed</span>
            </div>
        </div>
        <p>${esc(router.reason || 'The route records what Nexus selected and what actually executed for this run.')}</p>
        <div class="context-router-grid">
            <div><span>Selected route</span>${chips([...(selected.crews || router.selectedCrew || []), ...(selected.specialists || router.specialists || [])], 24)}</div>
            <div><span>Executable bindings</span>${chips([...(selected.actions || []), ...(selected.verifyCommands || router.verifyCommands || [])], 24)}</div>
            <div><span>Changed files</span>${chips(changedFiles, 24)}</div>
        </div>
    </section>`;
}

function learningPanel(learning) {
    const packets = learning?.packets || [];
    if (!packets.length) return '';
    return `<section class="context-learning-card">
        <div class="context-router-head">
            <div>
                <span>Nexus Prime Learning POD</span>
                <strong>${esc(packets.length)} packet${packets.length === 1 ? '' : 's'}</strong>
            </div>
            <button class="btn btn-sm" id="context-open-learning-btn">Open Learning</button>
        </div>
        <div class="context-learning-list">
            ${packets.slice(0, 4).map(packet => `<div class="context-learning-item">
                <span>${esc(packet.action)} · ${esc(packet.scope)} · ${Math.round(Number(packet.confidence || 0) * 100)}%</span>
                <strong>${esc(packet.lesson)}</strong>
                <em>${esc(packet.nextRunEffect || packet.next_run_effect || '')}</em>
            </div>`).join('')}
        </div>
    </section>`;
}

function contextRows(entries) {
    if (!entries?.length) {
        return '<div class="empty"><div class="empty-title">No context events for this run</div></div>';
    }
    return entries.map(entry => {
        const refs = [
            ...(entry.contextRefs || []),
            ...(entry.memoryRefs || []),
            ...(entry.fileRefs || []),
            ...(entry.evidenceRefs || []),
        ];
        const changed = entry.changedFiles || [];
        return `<article class="context-log-row">
            <div class="context-log-row-head">
                <span>${esc(entry.phase || 'context')}</span>
                <span>${esc(entry.actor || 'system')}</span>
                <time>${esc(fmtTs(entry.createdAt))}</time>
            </div>
            <p>${esc(entry.reason || '')}</p>
            <div class="context-log-refs">${chips(refs, 24)}</div>
            ${changed.length ? `<div class="context-log-changed"><span>Changed</span>${chips(changed, 16)}</div>` : ''}
        </article>`;
    }).join('');
}

function decisionRows(entries) {
    if (!entries?.length) {
        return '<div class="empty"><div class="empty-title">No decisions for this run</div></div>';
    }
    return `<div class="context-log-decision-list">${entries.map(entry => `
        <article class="context-log-decision">
            <div class="context-log-row-head">
                <span>${esc(entry.verb || 'decision')}</span>
                <span>${esc(entry.surface || entry.actor || 'runtime')}</span>
                <time>${esc(fmtTs(entry.createdAt))}</time>
            </div>
            <strong>${esc(entry.decision || '')}</strong>
            <p>${esc(entry.reason || '')}</p>
            <div class="context-log-refs">${chips([entry.beforeRef, entry.afterRef].filter(Boolean), 6)}</div>
            ${entry.changedFiles?.length ? `<div class="context-log-changed"><span>Changed</span>${chips(entry.changedFiles, 16)}</div>` : ''}
        </article>
    `).join('')}</div>`;
}

function renderRunRail(runs) {
    if (!runs.length) return `<div class="empty">
        <div class="empty-title">No runs recorded</div>
        <div class="empty-sub">Run a goal from the command bar and its context spine will appear here.</div>
    </div>`;
    return runs.map(run => {
        const id = runIdOf(run);
        const active = id === S.contextLogSelectedRunId;
        return `<button class="context-log-run ${active ? 'active' : ''}" data-context-run="${esc(id)}">
            <span>${esc(String(id).slice(-10) || 'run')}</span>
            <small>${esc(run.stage || run.state || run.status || 'queued')} · ${esc(fmtTs(run.createdAt || run.startedAt || run.updatedAt))}</small>
        </button>`;
    }).join('');
}

function emptyContextMain() {
    return `<div class="context-log-summary context-log-empty-state">
        <div class="context-log-kpi"><span>Runs</span><strong>0</strong></div>
        <div class="context-log-kpi"><span>Context</span><strong>—</strong></div>
        <div class="context-log-kpi"><span>Decision</span><strong>—</strong></div>
        <div class="context-log-selection">
            <div>
                <span>Start</span>
                <button class="btn btn-sm" id="context-log-run-goal-btn">Run a goal</button>
            </div>
            <div>
                <span>Expected here</span>
                ${chips(['request brief', 'selection plan', 'context events', 'decisions'])}
            </div>
            <div>
                <span>History</span>
                <span class="context-log-empty">Previous runs persist after the dashboard restarts.</span>
            </div>
        </div>
    </div>`;
}

export async function load() {
    const runs = await api('/api/runs?limit=30', 5_000, { timeoutMs: 3_500 });
    S.contextLogRuns = Array.isArray(runs) ? runs : [];
    if (!S.contextLogSelectedRunId) {
        S.contextLogSelectedRunId = runIdOf(S.contextLogRuns[0]) || null;
    }
    if (S.contextLogSelectedRunId) {
        const [spine, learning] = await Promise.all([
            api(spineUrl(S.contextLogSelectedRunId), 0, { timeoutMs: 3_500 }),
            api(learningUrl(S.contextLogSelectedRunId), 0, { timeoutMs: 3_500 }),
        ]);
        S.contextLogSpine = spine;
        S.contextLogLearning = learning;
    } else {
        S.contextLogSpine = null;
        S.contextLogLearning = null;
    }
    render();
}

export function render() {
    const host = $('context-log-view');
    if (!host) return;
    const spine = S.contextLogSpine;
    const artifacts = spine?.artifacts || {};
    const contextLog = artifacts.contextLog || [];
    const decisionLog = artifacts.decisionLog || [];
    host.innerHTML = `<div class="context-log-shell">
        <aside class="context-log-rail">
            <div class="context-log-rail-head">
                <span>Runs</span>
                <button class="btn btn-sm" id="context-log-refresh-btn">Refresh</button>
            </div>
            ${renderRunRail(S.contextLogRuns || [])}
        </aside>
        <section class="context-log-main">
            ${spine ? summaryCard(spine) : emptyContextMain()}
            ${spine ? routerPanel(spine) : ''}
            ${spine ? learningPanel(S.contextLogLearning) : ''}
            <div class="context-log-grid">
                <div>
                    <div class="shd">Context events</div>
                    ${contextRows(contextLog)}
                </div>
                <div>
                    <div class="shd">Decisions</div>
                    ${decisionRows(decisionLog)}
                </div>
            </div>
        </section>
    </div>`;
    host.querySelectorAll('[data-context-run]').forEach(button => {
        button.addEventListener('click', async () => {
            S.contextLogSelectedRunId = button.dataset.contextRun || null;
            if (S.contextLogSelectedRunId) bustCache(spineUrl(S.contextLogSelectedRunId));
            await load();
        });
    });
    $('context-log-run-goal-btn')?.addEventListener('click', () => {
        window.location.hash = '#board';
        setTimeout(() => $('cmd-input')?.focus(), 100);
    });
    $('context-open-learning-btn')?.addEventListener('click', () => {
        window.location.hash = '#learning';
    });
    host.querySelectorAll('[data-context-target]').forEach(button => {
        button.addEventListener('click', () => {
            const target = button.dataset.contextTarget;
            const value = button.dataset.contextValue || '';
            if (target === 'memory') {
                S.memQuery = value.replace(/^memory:/, '');
                window.location.hash = '#memory';
            } else if (target === 'repo') {
                window.location.hash = '#repo';
                setTimeout(() => {
                    const input = $('repo-search-input');
                    if (input) {
                        input.value = value.replace(/^.*\//, '');
                        input.dispatchEvent(new Event('input'));
                    }
                }, 120);
            }
        });
    });
    $('context-log-refresh-btn')?.addEventListener('click', async () => {
        bustCache('/api/runs?limit=30');
        if (S.contextLogSelectedRunId) bustCache(spineUrl(S.contextLogSelectedRunId));
        if (S.contextLogSelectedRunId) bustCache(learningUrl(S.contextLogSelectedRunId));
        await load();
    });
}
