/**
 * learning.js — Learning Network / POD dashboard.
 */

import { S } from '../state.js';
import { api, post, 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 fmtPct(value) {
    if (value == null || Number.isNaN(Number(value))) return '—';
    return `${Math.round(Number(value) * 100)}%`;
}

function packetTone(packet) {
    if (packet.action === 'promote') return 'good';
    if (packet.action === 'test') return 'info';
    if (packet.action === 'decay') return 'warn';
    return 'bad';
}

function runLabel(runId) {
    return String(runId || '').replace(/^exec_/, '').slice(-10) || 'run';
}

function evidenceChips(packet) {
    const refs = packet.evidenceRefs || packet.evidence || [];
    if (!refs.length) return '<span class="learning-empty">No evidence refs</span>';
    return refs.slice(0, 8).map(ref => {
        const raw = String(ref);
        const target = raw.includes('changed-files') || raw.includes('file:') ? 'repo' : raw.includes('memory') ? 'memory' : 'context-log';
        return `<button class="learning-chip" data-learning-target="${target}" data-learning-ref="${esc(raw)}">${esc(raw.replace(/^.*:/, ''))}</button>`;
    }).join('');
}

function packetCard(packet) {
    return `<article class="learning-packet learning-${packetTone(packet)}" data-packet-id="${esc(packet.id)}">
        <div class="learning-packet-head">
            <span class="learning-action">${esc(packet.action)}</span>
            <span>${esc(packet.scope)}</span>
            <span>${fmtPct(packet.confidence)}</span>
            <button class="learning-mini-btn" data-learning-run="${esc(packet.runId)}">Run</button>
        </div>
        <h3>${esc(packet.lesson)}</h3>
        <p>${esc(packet.nextRunEffect || packet.next_run_effect || '')}</p>
        <div class="learning-evidence">${evidenceChips(packet)}</div>
        <div class="learning-feedback">
            <button data-feedback="accepted" data-feedback-packet="${esc(packet.id)}" data-feedback-run="${esc(packet.runId)}">Accepted</button>
            <button data-feedback="useful" data-feedback-packet="${esc(packet.id)}" data-feedback-run="${esc(packet.runId)}">Useful</button>
            <button data-feedback="rejected" data-feedback-packet="${esc(packet.id)}" data-feedback-run="${esc(packet.runId)}">Wrong</button>
            <button data-feedback="ignore" data-feedback-packet="${esc(packet.id)}" data-feedback-run="${esc(packet.runId)}">Ignore</button>
        </div>
    </article>`;
}

function renderGraph(surface) {
    const svg = $('learning-graph-svg');
    if (!svg) return;
    const graph = surface?.graph || { nodes: [], edges: [] };
    const nodes = (graph.nodes || []).slice(0, 80).map(node => ({ ...node }));
    const edges = (graph.edges || []).slice(0, 140).map(edge => ({ ...edge }));
    svg.innerHTML = '';
    if (!nodes.length || typeof d3 === 'undefined') {
        svg.innerHTML = '<text x="24" y="36" fill="#a1a1aa" font-size="12">Learning graph appears after runs produce packets.</text>';
        return;
    }
    const host = $('learning-graph');
    const W = host?.clientWidth || 780;
    const H = host?.clientHeight || 360;
    const d3s = d3.select(svg).attr('viewBox', `0 0 ${W} ${H}`).attr('width', W).attr('height', H);
    const sim = d3.forceSimulation(nodes)
        .force('link', d3.forceLink(edges).id(d => d.id).distance(d => d.type === 'supported-by' ? 58 : 88).strength(0.35))
        .force('charge', d3.forceManyBody().strength(d => d.type === 'packet' ? -210 : -95))
        .force('center', d3.forceCenter(W / 2, H / 2))
        .force('collide', d3.forceCollide(d => d.type === 'packet' ? 32 : 20));
    const link = d3s.append('g').selectAll('line').data(edges).enter().append('line')
        .attr('stroke', d => d.type === 'changes' ? '#00ff88' : d.type === 'feedback' ? '#ffd14d' : '#3f3f46')
        .attr('stroke-opacity', 0.5);
    const node = d3s.append('g').selectAll('g').data(nodes).enter().append('g').attr('class', 'learning-node');
    node.append('circle')
        .attr('r', d => d.type === 'packet' ? 11 : d.type === 'run' ? 8 : 5)
        .attr('fill', d => d.type === 'packet' ? '#00d4ff' : d.type === 'policy' ? '#00ff88' : d.type === 'feedback' ? '#ffd14d' : '#8b5cf6')
        .attr('fill-opacity', d => d.type === 'evidence' ? 0.62 : 0.9);
    node.append('title').text(d => d.label);
    sim.on('tick', () => {
        link
            .attr('x1', d => d.source.x).attr('y1', d => d.source.y)
            .attr('x2', d => d.target.x).attr('y2', d => d.target.y);
        node.attr('transform', d => `translate(${Math.max(12, Math.min(W - 12, d.x))},${Math.max(12, Math.min(H - 12, d.y))})`);
    });
}

function renderPacketDrawer(surface) {
    const packets = surface?.packets || [];
    const selected = packets.find(packet => packet.id === S.learningSelectedPacketId) || packets[0];
    if (!selected) return '<div class="learning-empty-panel">No learning packet selected</div>';
    return `<div class="learning-drawer">
        <div class="shd">Learning packet</div>
        <h2>${esc(selected.lesson)}</h2>
        <div class="learning-score-row">
            <span>${esc(selected.action)}</span>
            <span>${esc(selected.scope)}</span>
            <span>${fmtPct(selected.confidence)}</span>
        </div>
        <p>${esc(selected.nextRunEffect || selected.next_run_effect || '')}</p>
        <div class="shd">Evidence</div>
        <div class="learning-evidence learning-evidence-large">${evidenceChips(selected)}</div>
    </div>`;
}

export async function load() {
    S.learningSurface = await api('/api/learning/packets?limit=24', 0, { timeoutMs: 4_500 });
    S.learningGraph = S.learningSurface?.graph || null;
    render();
}

export function render() {
    const host = $('learning-view');
    if (!host) return;
    const surface = S.learningSurface || {};
    const packets = surface.packets || [];
    const quality = surface.quality || {};
    host.innerHTML = `<div class="learning-shell">
        <section class="learning-hero">
            <div>
                <span class="learning-eyebrow">Nexus Learning Network</span>
                <h1>Memory is evidence. Learning changes the next run.</h1>
            </div>
            <div class="learning-kpis">
                <div><span>Runs</span><strong>${esc(quality.runs ?? 0)}</strong></div>
                <div><span>Packets</span><strong>${esc(quality.packets ?? packets.length)}</strong></div>
                <div><span>Promoted</span><strong>${esc(quality.promoted ?? 0)}</strong></div>
                <div><span>Testing</span><strong>${esc(quality.testing ?? 0)}</strong></div>
            </div>
        </section>
        <section class="learning-grid">
            <div>
                <div class="shd">Evidence graph</div>
                <div id="learning-graph" class="learning-graph card"><svg id="learning-graph-svg"></svg></div>
                <div class="shd">Recent packets</div>
                <div class="learning-packets">${packets.length ? packets.map(packetCard).join('') : '<div class="empty"><div class="empty-title">No learning packets yet</div><div class="empty-sub">Run a goal and Nexus will compile learning from decisions, files, workers, tests, and feedback.</div></div>'}</div>
            </div>
            ${renderPacketDrawer(surface)}
        </section>
    </div>`;
    renderGraph(surface);
    host.querySelectorAll('[data-packet-id]').forEach(card => {
        card.addEventListener('click', () => {
            S.learningSelectedPacketId = card.dataset.packetId;
            render();
        });
    });
    host.querySelectorAll('[data-learning-run]').forEach(button => {
        button.addEventListener('click', (ev) => {
            ev.stopPropagation();
            S.contextLogSelectedRunId = button.dataset.learningRun;
            window.location.hash = '#context-log';
        });
    });
    host.querySelectorAll('[data-learning-target]').forEach(button => {
        button.addEventListener('click', (ev) => {
            ev.stopPropagation();
            const target = button.dataset.learningTarget;
            const ref = button.dataset.learningRef || '';
            if (target === 'repo') {
                window.location.hash = '#repo';
                setTimeout(() => {
                    const input = $('repo-search-input');
                    if (input) {
                        input.value = ref.replace(/^.*:/, '').replace(/^.*\//, '');
                        input.dispatchEvent(new Event('input'));
                    }
                }, 120);
                return;
            }
            if (target === 'memory') {
                S.memQuery = ref;
                window.location.hash = '#memory';
                return;
            }
            S.contextLogSelectedRunId = ref.split(':')[0] || S.contextLogSelectedRunId;
            window.location.hash = '#context-log';
        });
    });
    host.querySelectorAll('[data-feedback]').forEach(button => {
        button.addEventListener('click', async (ev) => {
            ev.stopPropagation();
            const result = await post('/api/learning/feedback', {
                runId: button.dataset.feedbackRun,
                packetId: button.dataset.feedbackPacket,
                rating: button.dataset.feedback,
            });
            if (result.ok) {
                bustCache('/api/learning/packets?limit=24');
                await load();
            }
        });
    });
}
