import { URL } from 'url';
import { nexusEventBus } from '../../engines/event-bus.js';
import { getSharedGovernor } from '../../engines/machine-efficiency/index.js';
export function normalizeDashboardEvent(event) {
    const category = mapEventCategory(event.type);
    const severity = mapEventSeverity(event.type, event.data);
    return {
        id: event.id,
        type: event.type,
        title: mapEventTitle(event.type),
        source: mapEventSource(event.type, event.data),
        time: event.timestamp,
        severity,
        category,
        summary: summarizeEvent(event.type, event.data),
        payload: event.data,
    };
}
function isDashboardNoiseEvent(event) {
    const payload = (event.data || {});
    // Tier promotions are always shown because they represent memory getting better.
    if (event.type === 'memory.tier.promoted')
        return false;
    if (event.type === 'memory.store') {
        const tags = Array.isArray(payload.tags) ? payload.tags.map(String) : [];
        const priority = Number(payload.priority ?? 0);
        if (priority <= 0.3 && (tags.includes('#auto')
            || tags.includes('#welcome')
            || tags.includes('#demo'))) {
            return true;
        }
    }
    if (event.type === 'nexusnet.sync' && Number(payload.newItemsCount ?? 0) === 0) {
        return true;
    }
    return false;
}
export function getDashboardEventCards() {
    return nexusEventBus.getHistory()
        .filter((event) => !isDashboardNoiseEvent(event))
        .map((event) => normalizeDashboardEvent(event))
        .reverse();
}
export class SseBroker {
    clients = new Map();
    unsubscribeBus = null;
    broadcastThrottles = new Map();
    lastSignalByType = new Map();
    start() {
        if (this.unsubscribeBus) {
            return;
        }
        this.unsubscribeBus = nexusEventBus.onEvent((event) => this.broadcast(event));
        nexusEventBus.startFilePolling();
    }
    stop() {
        if (this.unsubscribeBus) {
            this.unsubscribeBus();
            this.unsubscribeBus = null;
        }
        nexusEventBus.stopFilePolling();
        const governor = getSharedGovernor();
        for (const res of this.clients.keys()) {
            res.end();
            governor.releaseDashboardListener();
        }
        this.clients.clear();
    }
    getClientCount() {
        return this.clients.size;
    }
    serve(req, res, corsOrigin) {
        const governor = getSharedGovernor();
        if (!governor.registerDashboardListener()) {
            const body = JSON.stringify({
                error: 'dashboard-stream-cap-reached',
                message: 'Maximum active dashboard subscriptions reached.',
                maxDashboardListeners: governor.getState().maxDashboardListeners,
            });
            res.writeHead(503, {
                'Content-Type': 'application/json',
                'Cache-Control': 'no-cache',
                'Access-Control-Allow-Origin': corsOrigin,
            });
            res.end(body);
            return;
        }
        // Wire abort handlers BEFORE the rest of init so a fast disconnect during
        // bootstrap still releases the listener slot.
        let released = false;
        // Holder so the abort handlers, registered before the timer is created,
        // can still clear it once setInterval fires below.
        const pingTimer = {};
        const releaseOnce = () => {
            if (released)
                return;
            released = true;
            if (pingTimer.value)
                clearInterval(pingTimer.value);
            this.clients.delete(res);
            governor.releaseDashboardListener();
        };
        req.on('close', releaseOnce);
        res.on('error', releaseOnce);
        res.on('close', releaseOnce);
        let runtimeId = null;
        try {
            const reqUrl = new URL(req.url ?? '/', 'http://localhost');
            const raw = reqUrl.searchParams.get('runtimeId');
            runtimeId = raw && raw.trim().length > 0 ? raw.trim() : null;
        }
        catch {
            runtimeId = null;
        }
        res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
            'Access-Control-Allow-Origin': corsOrigin,
        });
        const safeWrite = (chunk) => {
            if (!res.writable || res.destroyed)
                return false;
            try {
                return res.write(chunk);
            }
            catch {
                return false;
            }
        };
        safeWrite('retry: 3000\n\n');
        safeWrite(`event: bootstrap\ndata: ${JSON.stringify({ connected: true, timestamp: Date.now(), version: '4', runtimeId })}\n\n`);
        for (const event of getDashboardEventCards()) {
            const eventRuntimeId = this.extractRuntimeId(event.payload);
            if (runtimeId && eventRuntimeId && eventRuntimeId !== runtimeId)
                continue;
            if (!safeWrite(`data: ${JSON.stringify(event)}\n\n`))
                break;
        }
        this.clients.set(res, { runtimeId });
        pingTimer.value = setInterval(() => {
            if (!safeWrite(':\n\n')) {
                releaseOnce();
            }
        }, 20_000);
        pingTimer.value.unref();
    }
    extractRuntimeId(payload) {
        if (!payload || typeof payload !== 'object')
            return null;
        const candidate = payload.runtimeId ?? payload.workspaceId ?? payload.stateKey;
        if (typeof candidate === 'string' && candidate.trim().length > 0)
            return candidate.trim();
        return null;
    }
    throttleIntervalFor(type) {
        if (type === 'client.heartbeat')
            return 10_000;
        if (type === 'pod.signal')
            return 5_000;
        if (type === 'memory.store')
            return 2_000;
        if (type === 'memory.recall')
            return 2_000;
        if (type === 'nexusnet.sync')
            return 5_000;
        return 0;
    }
    eventSummaryKey(event) {
        return `${event.type}:${summarizeEvent(event.type, event.data)}`;
    }
    isNoiseEvent(event) {
        const payload = (event.data || {});
        if (isDashboardNoiseEvent(event))
            return true;
        if (event.type === 'pod.signal') {
            const summaryKey = this.eventSummaryKey(event);
            if (!payload || Object.keys(payload).length === 0)
                return true;
            if (!String(payload.content ?? '').trim())
                return true;
            const previous = this.lastSignalByType.get(event.type);
            this.lastSignalByType.set(event.type, summaryKey);
            if (previous && previous === summaryKey)
                return true;
        }
        return false;
    }
    broadcast(event) {
        const now = Date.now();
        const throttleInterval = this.throttleIntervalFor(event.type);
        if (throttleInterval > 0) {
            const last = this.broadcastThrottles.get(event.type) ?? 0;
            if (now - last < throttleInterval) {
                return;
            }
            this.broadcastThrottles.set(event.type, now);
        }
        if (this.isNoiseEvent(event) && mapEventSeverity(event.type, event.data) !== 'bad') {
            return;
        }
        const normalized = normalizeDashboardEvent(event);
        const data = `data: ${JSON.stringify(normalized)}\n\n`;
        const eventRuntimeId = this.extractRuntimeId(event.data);
        const dead = [];
        for (const [res, meta] of this.clients) {
            // Events without a runtime ID (system-wide: boot, license, federation) broadcast to all clients.
            // Events with a runtime ID skip clients scoped to a different runtime; unscoped clients still receive them.
            if (eventRuntimeId && meta.runtimeId && eventRuntimeId !== meta.runtimeId)
                continue;
            try {
                res.write(data);
            }
            catch {
                // Client disconnected mid-broadcast.  Evict so we don't throw on every
                // subsequent event — this was a daemon-crashing bug before the fix.
                dead.push(res);
            }
        }
        if (dead.length) {
            const governor = getSharedGovernor();
            for (const res of dead) {
                if (this.clients.delete(res)) {
                    governor.releaseDashboardListener();
                }
            }
        }
    }
}
function mapEventCategory(type) {
    if (type === 'tool.invocation')
        return 'mcp';
    if (type.startsWith('mcp.'))
        return 'mcp';
    if (type.startsWith('planner.'))
        return 'runtime';
    if (type.startsWith('memory.'))
        return 'memory';
    if (type.startsWith('pod.'))
        return 'pod';
    if (type.startsWith('phantom.'))
        return 'runtime';
    if (type.startsWith('client.'))
        return 'clients';
    if (type.startsWith('skill.'))
        return 'skills';
    if (type.startsWith('hook.'))
        return 'hooks';
    if (type.startsWith('workflow.'))
        return 'workflows';
    if (type.startsWith('automation.'))
        return 'automations';
    if (type.startsWith('shield.'))
        return 'shield';
    if (type.startsWith('federation.') || type.startsWith('nexusnet.'))
        return 'federation';
    if (type.startsWith('tokens.') || type.startsWith('cas.') || type.startsWith('kv.'))
        return 'tokens';
    if (type.startsWith('byzantine.'))
        return 'byzantine';
    if (type.startsWith('darwin.'))
        return 'darwin';
    return 'system';
}
function mapEventSeverity(type, payload) {
    if (type === 'mcp.call.start')
        return 'info';
    if (type === 'mcp.call.complete')
        return payload.error ? 'bad' : 'good';
    if (type === 'guardrail.check') {
        return payload.passed ? 'good' : 'bad';
    }
    if (type === 'phantom.merge' || type === 'phantom.merge.complete' || type === 'workflow.run') {
        return payload.status === 'failed' ? 'bad' : 'good';
    }
    if (type === 'client.inferred')
        return 'warn';
    if (type === 'dashboard.action' && payload.status === 'failed')
        return 'bad';
    if (type === 'pod.signal')
        return 'info';
    if (type === 'shield.decision')
        return payload.blocked ? 'bad' : payload.action === 'quarantine' ? 'warn' : 'info';
    return 'info';
}
function mapEventTitle(type) {
    return {
        'system.boot': 'Runtime boot',
        'planner.stage': 'Planner stage',
        'memory.store': 'Memory stored',
        'memory.recall': 'Memory recall',
        'pod.signal': 'POD signal',
        'tokens.optimized': 'Tokens optimized',
        'phantom.worker.start': 'Worker start',
        'phantom.worker.complete': 'Worker complete',
        'phantom.merge.complete': 'Merge complete',
        'phantom.merge': 'Merge decision',
        'guardrail.check': 'Guardrail check',
        'ghost.pass': 'Ghost pass',
        'graph.query': 'Graph query',
        'darwin.cycle': 'Darwin cycle',
        'session.dna': 'Session DNA',
        'skill.register': 'Skill registered',
        'skill.deploy': 'Skill deployed',
        'skill.revoke': 'Skill revoked',
        'hook.deploy': 'Hook deployed',
        'hook.revoke': 'Hook revoked',
        'hook.fire': 'Hook fired',
        'workflow.deploy': 'Workflow deployed',
        'workflow.run': 'Workflow run',
        'automation.deploy': 'Automation deployed',
        'automation.revoke': 'Automation revoked',
        'automation.run': 'Automation run',
        'shield.decision': 'Shield decision',
        'memory.audit': 'Memory audit',
        'federation.heartbeat': 'Federation heartbeat',
        'client.heartbeat': 'Client heartbeat',
        'client.inferred': 'Client inferred',
        'client.status': 'Client status',
        'dashboard.action': 'Dashboard action',
        'nexusnet.publish': 'NexusNet publish',
        'nexusnet.sync': 'NexusNet sync',
        'mcp.call.start': 'MCP execute',
        'mcp.call.complete': 'MCP result',
        'entanglement.create': 'Entanglement created',
        'entanglement.collapse': 'Entanglement collapsed',
        'entanglement.correlate': 'Entanglement correlated',
        'cas.encode': 'CAS encode',
        'cas.decode': 'CAS decode',
        'cas.pattern_learned': 'CAS pattern',
        'kv.merge': 'KV merge',
        'kv.adapt': 'KV adapt',
        'kv.consensus': 'KV consensus',
        'byzantine.vote.started': 'Consensus started',
        'byzantine.vote.result': 'Consensus result',
        'darwin.cycle.complete': 'Darwin cycle complete',
    }[type] ?? type;
}
function mapEventSource(type, payload) {
    if (type.startsWith('mcp.'))
        return String(payload.toolName ?? payload.serverName ?? 'mcp');
    if (type.startsWith('planner.'))
        return String(payload.owner ?? payload.runId ?? 'planner');
    if (type.startsWith('client.'))
        return String(payload.displayName ?? payload.clientId ?? 'client');
    if (type === 'pod.signal')
        return String(payload.workerId ?? 'pod');
    if (type.startsWith('phantom.'))
        return String(payload.workerId ?? payload.winner ?? 'runtime');
    if (type.startsWith('skill.'))
        return String(payload.skillId ?? payload.name ?? 'skill');
    if (type.startsWith('hook.'))
        return String(payload.hookId ?? payload.name ?? 'hook');
    if (type.startsWith('workflow.'))
        return String(payload.workflowId ?? 'workflow');
    if (type.startsWith('automation.'))
        return String(payload.automationId ?? 'automation');
    if (type.startsWith('shield.'))
        return String(payload.target ?? 'shield');
    if (type.startsWith('federation.'))
        return String(payload.peerId ?? 'federation');
    return 'nexus-prime';
}
function summarizeEvent(type, payload) {
    switch (type) {
        case 'planner.stage':
            return `${payload.stage ?? 'stage'} · ${payload.status ?? 'unknown'} · ${payload.assets ?? 0} asset(s)`;
        case 'memory.store':
            return `Priority ${payload.priority ?? 'n/a'} · ${payload.tags?.join(', ') ?? 'no tags'}`;
        case 'memory.recall':
            return `Recalled ${payload.count ?? 0} memories for "${payload.query ?? ''}"`;
        case 'pod.signal':
            return String(payload.content ?? 'POD signal received');
        case 'tokens.optimized': {
            const src = payload.source ? ` · ${payload.source}` : '';
            return `Saved ${payload.savings ?? 0} tokens across ${payload.files ?? 0} files${src}`;
        }
        case 'phantom.worker.start':
            return `${payload.approach ?? 'worker'} started for ${payload.goal ?? 'task'}`;
        case 'phantom.worker.complete':
            return `Confidence ${payload.confidence ?? 0}`;
        case 'mcp.call.start':
            return `Tool ${payload.toolName ?? 'unknown'} initialized`;
        case 'mcp.call.complete':
            return payload.error
                ? `Fault: ${payload.error}`
                : `${payload.durationMs ?? 0}ms [${payload.resultByteSize ?? 0} B]`;
        case 'phantom.merge':
            return `${payload.action ?? 'merge'} · ${payload.winner ?? 'unknown winner'}`;
        case 'client.heartbeat':
        case 'client.inferred':
        case 'client.status':
            return JSON.stringify(payload);
        case 'hook.fire':
            return `${payload.name ?? payload.hookId ?? 'hook'} @ ${payload.trigger ?? 'unknown trigger'}`;
        case 'automation.run':
            return `${payload.automationId ?? 'automation'} @ ${payload.trigger ?? 'unknown trigger'}${payload.queued ? ' queued' : ''}`;
        case 'shield.decision':
            return `${payload.action ?? 'allow'} · ${payload.target ?? 'unknown target'}`;
        case 'memory.audit':
            return `Scanned ${payload.scanned ?? 0} memories · quarantined ${payload.quarantined ?? 0}`;
        case 'federation.heartbeat':
            return `${payload.peerId ?? 'peer'} · ${payload.health ?? 'unknown'} · ${payload.capabilities ?? 0} capabilities`;
        case 'byzantine.vote.started':
            return `Proposal ${String(payload.proposalId ?? '').slice(-6)} · ${payload.agentCount ?? 0} agents · quorum ${payload.quorum ?? 0}`;
        case 'byzantine.vote.result':
            return `${payload.votesFor ?? 0}✓ ${payload.votesAgainst ?? 0}✗ · ${payload.accepted ? 'accepted' : 'rejected'}`;
        case 'dashboard.action':
            return `${payload.action ?? 'action'} → ${payload.status ?? 'unknown'}`;
        case 'install.step':
            return `Step ${payload.step ?? '?'}/${payload.total ?? '?'} · ${payload.label ?? ''} · ${payload.status ?? ''}`;
        case 'license.tierChanged':
            return `Tier: ${payload.from ?? '?'} → ${payload.to ?? '?'} (${payload.reason ?? ''})`;
        case 'license.upgradeNudge':
            return `Upgrade nudge · ${payload.currentTier ?? 'free'} · ${payload.feature ?? ''}`;
        case 'orchestrator.run.start':
            return `Run started · ${String(payload.goal ?? '').slice(0, 60)}`;
        case 'orchestrator.run.complete':
            return `Run complete · ${payload.durationMs ? `${payload.durationMs}ms` : ''}`;
        default:
            return JSON.stringify(payload);
    }
}
