/**
 * Phylax UI - Failure-First Inspector (Phase 11)
 * 
 * DESIGN PRINCIPLE: When something breaks, Phylax explains why faster than a human can.
 * 
 * - Opens in FAILED-ONLY mode by default
 * - Failure summary above everything
 * - Navigation between failed traces only
 * - No distractions during damage control
 */

const API_BASE = '/v1';
let currentTraceId = null;
let traces = [];
let failedTraces = [];
let currentFailedIndex = -1;
let currentFilter = 'failed'; // DEFAULT: Failed-only mode (Phase 11.1)
let visibleCount = 5;
const PAGE_SIZE = 5;

// Phase 22: Failure Forensics Mode
let forensicsMode = false;

/**
 * Initialize the application
 */
document.addEventListener('DOMContentLoaded', () => {
    loadTraces();
});

/**
 * Load traces from the API
 */
async function loadTraces() {
    const traceList = document.getElementById('traceList');
    traceList.innerHTML = `
        <div class="loading">
            <div class="spinner"></div>
        </div>
    `;

    try {
        const response = await fetch(`${API_BASE}/traces?limit=100`);
        const data = await response.json();

        traces = data.traces || [];
        failedTraces = traces.filter(t => t.verdict?.status === 'fail');
        visibleCount = PAGE_SIZE;

        updateStats();
        renderTraceList();

        // Phase 11.1: Auto-select first failed trace if exists
        if (failedTraces.length > 0 && currentFilter === 'failed') {
            selectTrace(failedTraces[0].trace_id);
        }
    } catch (error) {
        traceList.innerHTML = `
            <div class="empty-state">
                <div class="empty-state-icon">⚠️</div>
                <h3>Error loading traces</h3>
                <p>${error.message}</p>
            </div>
        `;
    }
}

/**
 * Update header stats
 */
function updateStats() {
    const failed = failedTraces.length;
    const passed = traces.filter(t => t.verdict?.status === 'pass').length;

    document.getElementById('failedCount').textContent = failed;
    document.getElementById('passedCount').textContent = passed;
    document.getElementById('totalCount').textContent = traces.length;
}

/**
 * Set filter and re-render
 */
function setFilter(filter, element) {
    currentFilter = filter;
    visibleCount = PAGE_SIZE;

    document.querySelectorAll('.filter-tab').forEach(tab => {
        tab.classList.remove('active');
    });
    if (element) {
        element.classList.add('active');
    }

    renderTraceList();

    // Auto-select first trace of new filter
    const filtered = getFilteredTraces();
    if (filtered.length > 0) {
        selectTrace(filtered[0].trace_id);
    }
}

/**
 * Get filtered traces
 */
function getFilteredTraces() {
    let result = traces;

    // Apply status filter
    if (currentFilter === 'failed') {
        result = failedTraces;
    } else if (currentFilter === 'passed') {
        result = traces.filter(t => t.verdict?.status === 'pass');
    }

    // Apply search filter
    if (searchQuery) {
        result = result.filter(t => t.trace_id.toLowerCase().includes(searchQuery));
    }

    return result;
}

/**
 * Load more traces
 */
function loadMore() {
    visibleCount += PAGE_SIZE;
    renderTraceList();
}

/**
 * Search traces by ID
 */
let searchQuery = '';

function searchTraces(query) {
    searchQuery = query.trim().toLowerCase();

    if (!searchQuery) {
        // If search is cleared, render normally
        renderTraceList();
        return;
    }

    // Check for exact match first
    const exactMatch = traces.find(t => t.trace_id.toLowerCase() === searchQuery);
    if (exactMatch) {
        selectTrace(exactMatch.trace_id);
        return;
    }

    // Otherwise filter and render matching traces
    renderTraceList();
}

/**
 * Render the trace list
 */
function renderTraceList() {
    const traceList = document.getElementById('traceList');
    const filteredTraces = getFilteredTraces();

    // Phase 11.1: Green banner when no failures
    if (currentFilter === 'failed' && filteredTraces.length === 0) {
        traceList.innerHTML = `
            <div class="success-banner">
                <div class="success-icon">✅</div>
                <h3>No regressions detected</h3>
                <p>All expectations are passing</p>
            </div>
        `;
        // Clear detail panel
        document.getElementById('traceDetail').innerHTML = `
            <div class="empty-state">
                <div class="empty-state-icon">🛡️</div>
                <h3>All clear</h3>
                <p>No failed traces to inspect</p>
            </div>
        `;
        return;
    }

    if (filteredTraces.length === 0) {
        traceList.innerHTML = `
            <div class="empty-state">
                <div class="empty-state-icon">📋</div>
                <h3>No traces</h3>
                <p>No traces match this filter</p>
            </div>
        `;
        return;
    }

    const visibleTraces = filteredTraces.slice(0, visibleCount);
    const hasMore = filteredTraces.length > visibleCount;

    let html = visibleTraces.map((trace, index) => {
        const time = formatTime(trace.timestamp);
        const preview = trace.request.messages?.[0]?.content?.substring(0, 50) || 'No message';
        const isActive = trace.trace_id === currentTraceId ? 'active' : '';

        let verdictClass = 'pending';
        let verdictIcon = '⏳';
        if (trace.verdict) {
            if (trace.verdict.status === 'pass') {
                verdictClass = 'pass';
                verdictIcon = '✓';
            } else {
                verdictClass = 'fail';
                verdictIcon = '✕';
            }
        }

        // Add blessed class for golden traces
        const blessedClass = trace.blessed ? 'blessed' : '';

        return `
            <div class="trace-item ${isActive} verdict-${verdictClass} ${blessedClass}" onclick="selectTrace('${trace.trace_id}')">
                <div class="trace-header">
                    <div class="verdict-badge ${verdictClass}">${verdictIcon}</div>
                    <span class="trace-model">${trace.request.model}</span>
                    ${trace.blessed ? '<span class="blessed-badge">GOLDEN</span>' : ''}
                </div>
                <div class="trace-preview">${escapeHtml(preview)}...</div>
                <div class="trace-meta">
                    <span>⏱ ${trace.response.latency_ms}ms</span>
                    <span>🕐 ${time}</span>
                </div>
            </div>
        `;
    }).join('');

    if (hasMore) {
        const remaining = filteredTraces.length - visibleCount;
        html += `
            <button class="load-more-btn" onclick="loadMore()">
                Load More (${remaining} remaining)
            </button>
        `;
    }

    traceList.innerHTML = html;
}

/**
 * Navigate to previous failed trace (Phase 11.4)
 */
function prevFailedTrace() {
    if (failedTraces.length === 0) return;

    currentFailedIndex = Math.max(0, currentFailedIndex - 1);
    selectTrace(failedTraces[currentFailedIndex].trace_id);
}

/**
 * Navigate to next failed trace (Phase 11.4)
 */
function nextFailedTrace() {
    if (failedTraces.length === 0) return;

    currentFailedIndex = Math.min(failedTraces.length - 1, currentFailedIndex + 1);
    selectTrace(failedTraces[currentFailedIndex].trace_id);
}

/**
 * Select and show a trace
 */
async function selectTrace(traceId) {
    currentTraceId = traceId;

    // Update failed index
    currentFailedIndex = failedTraces.findIndex(t => t.trace_id === traceId);

    const detail = document.getElementById('traceDetail');
    const replayBtn = document.getElementById('replayBtn');
    const navBtns = document.getElementById('failNav');

    renderTraceList();

    detail.innerHTML = `
        <div class="loading">
            <div class="spinner"></div>
        </div>
    `;

    try {
        const response = await fetch(`${API_BASE}/traces/${traceId}`);
        const trace = await response.json();

        replayBtn.disabled = false;

        // Update bless button state
        updateBlessButton();

        // Update navigation buttons
        if (navBtns) {
            navBtns.style.display = failedTraces.length > 1 ? 'flex' : 'none';
            document.getElementById('prevBtn').disabled = currentFailedIndex <= 0;
            document.getElementById('nextBtn').disabled = currentFailedIndex >= failedTraces.length - 1;
            document.getElementById('failCounter').textContent =
                `${currentFailedIndex + 1} / ${failedTraces.length}`;
        }

        // Build the detail view
        let html = '';

        // Phase 11.2: FAILURE SUMMARY FIRST (above everything)
        if (trace.verdict) {
            const isPass = trace.verdict.status === 'pass';
            const severity = trace.verdict.severity?.toUpperCase() || '';

            if (!isPass) {
                // Large failure banner
                html += `
                    <div class="failure-banner">
                        <div class="failure-header">
                            <div class="failure-icon">❌</div>
                            <div class="failure-title">
                                <h2>REGRESSION DETECTED</h2>
                                <span class="severity severity-${trace.verdict.severity}">${severity}</span>
                            </div>
                        </div>
                        
                        <div class="violations-section">
                            <h4>Violations:</h4>
                            <ul class="violations-list">
                                ${trace.verdict.violations.map(v => `
                                    <li><span class="violation-bullet">•</span> ${escapeHtml(v)}</li>
                                `).join('')}
                            </ul>
                        </div>
                        
                        ${trace.replay_of ? `
                            <div class="golden-ref">
                                <h4>Golden Reference:</h4>
                                <div class="golden-info">
                                    <span>Trace ID: ${trace.replay_of.substring(0, 20)}...</span>
                                </div>
                            </div>
                        ` : ''}
                    </div>
                `;

                // Phase 11.3: Inline diff for golden mismatches
                if (trace.replay_of) {
                    html += await renderDiff(trace);
                }
            } else {
                html += `
                    <div class="pass-banner">
                        <div class="pass-icon">✅</div>
                        <div>
                            <h3>PASSED</h3>
                            <p>All expectations met</p>
                        </div>
                    </div>
                `;
            }
        }

        // Trace details (below the failure summary)
        html += `
            <div class="trace-details">
                <div class="detail-section">
                    <h4 class="section-title">Request</h4>
                    <div class="trace-id">ID: ${trace.trace_id}</div>
                    <div class="model-info">${trace.request.model} (${trace.request.provider})</div>
                </div>
                
                <div class="detail-section">
                    <h4 class="section-title">Prompt</h4>
                    <div class="prompt-box">
                        ${trace.request.messages.map(msg => `
                            <div class="message ${msg.role}">
                                <span class="role">${msg.role}:</span>
                                <span class="content">${escapeHtml(msg.content)}</span>
                            </div>
                        `).join('')}
                    </div>
                </div>
                
                <div class="detail-section">
                    <h4 class="section-title">Response</h4>
                    <div class="response-box">${escapeHtml(trace.response.text)}</div>
                </div>
                
                <div class="detail-section meta-section">
                    <div class="meta-grid">
                        <div class="meta-item">
                            <span class="meta-label">Latency</span>
                            <span class="meta-value">${trace.response.latency_ms}ms</span>
                        </div>
                        <div class="meta-item">
                            <span class="meta-label">Timestamp</span>
                            <span class="meta-value">${formatTime(trace.timestamp)}</span>
                        </div>
                    </div>
                </div>
            </div>
        `;

        detail.innerHTML = html;

    } catch (error) {
        detail.innerHTML = `
            <div class="empty-state">
                <div class="empty-state-icon">⚠️</div>
                <h3>Error loading trace</h3>
                <p>${error.message}</p>
            </div>
        `;
    }
}

/**
 * Render diff between golden and current output (Phase 11.3)
 */
async function renderDiff(trace) {
    try {
        const goldenResponse = await fetch(`${API_BASE}/traces/${trace.replay_of}`);
        const goldenTrace = await goldenResponse.json();

        const goldenText = goldenTrace.response.text;
        const currentText = trace.response.text;

        if (goldenText === currentText) {
            return ''; // No diff needed
        }

        return `
            <div class="diff-section">
                <h4 class="section-title">What Changed?</h4>
                <div class="diff-container">
                    <div class="diff-panel">
                        <div class="diff-header diff-old">
                            <span>Expected (Golden)</span>
                        </div>
                        <div class="diff-content">${escapeHtml(goldenText)}</div>
                    </div>
                    <div class="diff-panel">
                        <div class="diff-header diff-new">
                            <span>Actual (Current)</span>
                        </div>
                        <div class="diff-content">${escapeHtml(currentText)}</div>
                    </div>
                </div>
            </div>
        `;
    } catch {
        return '';
    }
}

/**
 * Replay the current trace
 */
async function replayTrace() {
    if (!currentTraceId) return;

    const replayBtn = document.getElementById('replayBtn');
    replayBtn.disabled = true;
    replayBtn.innerHTML = '🔁 Replaying...';

    try {
        const response = await fetch(`${API_BASE}/replay/${currentTraceId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({})
        });

        const result = await response.json();

        if (response.ok) {
            showNotification('Replay complete', 'success');
            loadTraces();
            setTimeout(() => selectTrace(result.new_trace_id), 500);
        } else {
            showNotification(`Replay failed: ${result.detail}`, 'error');
        }
    } catch (error) {
        showNotification(`Error: ${error.message}`, 'error');
    } finally {
        replayBtn.disabled = false;
        replayBtn.innerHTML = '🔁 Replay';
    }
}

/**
 * Bless the current trace as a golden reference
 */
async function blessTrace() {
    if (!currentTraceId) return;

    const blessBtn = document.getElementById('blessBtn');
    const currentTrace = traces.find(t => t.trace_id === currentTraceId);

    // If already blessed, unbless
    if (currentTrace?.blessed) {
        blessBtn.disabled = true;
        blessBtn.innerHTML = '⭐ Unblessing...';

        try {
            const response = await fetch(`${API_BASE}/traces/${currentTraceId}/bless`, {
                method: 'DELETE'
            });

            if (response.ok) {
                showNotification('Golden status removed', 'success');
                loadTraces();
            } else {
                const result = await response.json();
                showNotification(`Error: ${result.detail}`, 'error');
            }
        } catch (error) {
            showNotification(`Error: ${error.message}`, 'error');
        } finally {
            blessBtn.disabled = false;
            blessBtn.innerHTML = '⭐ Bless as Golden';
        }
        return;
    }

    // Bless the trace
    blessBtn.disabled = true;
    blessBtn.innerHTML = '⭐ Blessing...';

    try {
        const response = await fetch(`${API_BASE}/traces/${currentTraceId}/bless`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' }
        });

        const result = await response.json();

        if (response.ok) {
            showNotification('✨ Trace blessed as golden reference!', 'success');
            loadTraces();
            setTimeout(() => selectTrace(currentTraceId), 500);
        } else {
            showNotification(`Bless failed: ${result.detail}`, 'error');
        }
    } catch (error) {
        showNotification(`Error: ${error.message}`, 'error');
    } finally {
        blessBtn.disabled = false;
        updateBlessButton();
    }
}

/**
 * Update bless button state based on current trace
 */
function updateBlessButton() {
    const blessBtn = document.getElementById('blessBtn');
    if (!blessBtn) return;

    const currentTrace = traces.find(t => t.trace_id === currentTraceId);

    if (!currentTrace) {
        blessBtn.disabled = true;
        blessBtn.innerHTML = '⭐ Bless as Golden';
        return;
    }

    if (currentTrace.blessed) {
        blessBtn.disabled = false;
        blessBtn.innerHTML = '⭐ Unbless';
        blessBtn.classList.add('is-blessed');
    } else {
        // Can only bless passing traces
        const canBless = currentTrace.verdict?.status === 'pass';
        blessBtn.disabled = !canBless;
        blessBtn.innerHTML = '⭐ Bless as Golden';
        blessBtn.classList.remove('is-blessed');
    }
}

/**
 * Show notification
 */
function showNotification(message, type = 'info') {
    const notification = document.createElement('div');
    notification.className = `notification notification-${type}`;
    notification.textContent = message;
    document.body.appendChild(notification);

    setTimeout(() => {
        notification.classList.add('fade-out');
        setTimeout(() => notification.remove(), 300);
    }, 3000);
}

/**
 * Format timestamp
 */
function formatTime(timestamp) {
    const date = new Date(timestamp);
    const now = new Date();
    const diff = now - date;

    const timeOpts = { hour: '2-digit', minute: '2-digit', hour12: true };

    if (diff < 60000) return 'Just now';
    if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
    if (diff < 86400000) return date.toLocaleTimeString('en-US', timeOpts);

    return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) +
        ', ' + date.toLocaleTimeString('en-US', timeOpts);
}

/**
 * Escape HTML
 */
function escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// =============================================================================
// Phase 15: Graph Visualization
// =============================================================================

let currentView = 'traces'; // 'traces' or 'graph'
let executions = [];
let currentGraph = null;

/**
 * Switch between Traces and Graph views
 */
function switchView(view) {
    currentView = view;

    // Update tabs
    document.querySelectorAll('.main-tab').forEach(t => t.classList.remove('active'));
    document.querySelector(`[data-view="${view}"]`)?.classList.add('active');

    const tracesView = document.getElementById('tracesView');
    const graphView = document.getElementById('graphView');

    if (view === 'traces') {
        tracesView.style.display = 'grid';
        graphView.style.display = 'none';
    } else {
        tracesView.style.display = 'none';
        graphView.style.display = 'grid';
        loadExecutions();
    }
}

/**
 * Load all executions
 */
async function loadExecutions() {
    const container = document.getElementById('executionList');
    container.innerHTML = '<div class="loading"><div class="spinner"></div></div>';

    try {
        const response = await fetch(`${API_BASE}/executions`);
        const data = await response.json();
        executions = data.executions || [];

        if (executions.length === 0) {
            container.innerHTML = `
                <div class="empty-state">
                    <div class="empty-state-icon">📊</div>
                    <h3>No executions yet</h3>
                    <p>Run code with Phylax.execution() context</p>
                </div>
            `;
            return;
        }

        container.innerHTML = executions.map(exec => {
            // Handle both old (string) and new (object) formats for backward compatibility
            const id = typeof exec === 'string' ? exec : exec.execution_id;
            const verdict = typeof exec === 'string' ? 'unknown' : exec.verdict;
            const blessed = typeof exec === 'string' ? false : exec.blessed;

            let statusClass = '';
            let icon = '';

            if (verdict === 'fail') {
                statusClass = 'fail';
                icon = '❌';
            } else if (verdict === 'pass') {
                statusClass = 'pass';
                icon = '✅';
            }

            if (blessed) {
                statusClass = 'blessed';
                icon = '⭐';
            }

            return `
            <div class="execution-item ${statusClass}" id="exec-${id}" onclick="loadGraph('${id}')" title="${id}">
                <div class="execution-id-row">
                    <span class="execution-status-icon">${icon}</span>
                    <span class="execution-id">${id.substring(0, 8)}...${id.substring(id.length - 4)}</span>
                </div>
                ${typeof exec !== 'string' ? `<span class="execution-meta">${formatTimeShort(exec.timestamp)}</span>` : ''}
            </div>
        `}).join('');

        // Auto-load first execution
        loadGraph(executions[0]);
    } catch (error) {
        container.innerHTML = `<div class="empty-state"><p>Error: ${error.message}</p></div>`;
    }
}

/**
 * Load and render a specific execution graph
 */
async function loadGraph(executionId) {
    const graphContainer = document.getElementById('graphCanvas');
    graphContainer.innerHTML = '<div class="loading"><div class="spinner"></div></div>';

    // Update selection in sidebar
    document.querySelectorAll('.execution-item').forEach(el => el.classList.remove('active'));
    const selectedItem = document.getElementById(`exec-${executionId}`);
    if (selectedItem) selectedItem.classList.add('active');

    try {
        const response = await fetch(`${API_BASE}/executions/${executionId}/graph`);

        if (!response.ok) {
            const error = await response.json();
            graphContainer.innerHTML = `
                <div class="empty-state">
                    <div class="empty-state-icon">📊</div>
                    <h3>Single-node execution</h3>
                    <p>This trace was captured without execution context.</p>
                    <p class="hint">Use <code>with Phylax.execution():</code> to create graphs.</p>
                </div>
            `;
            return;
        }

        const graph = await response.json();
        currentGraph = graph;

        renderGraph(graph);
    } catch (error) {
        graphContainer.innerHTML = `<div class="empty-state"><p>Error: ${error.message}</p></div>`;
    }
}

/**
 * Render the execution graph as a visual DAG
 */
function renderGraph(graph) {
    const container = document.getElementById('graphCanvas');

    if (!graph.nodes || graph.nodes.length === 0) {
        container.innerHTML = '<div class="empty-state"><p>Empty graph</p></div>';
        return;
    }

    // Get verdict for header
    const verdict = graph.verdict || { status: 'unknown' };
    const verdictIcon = verdict.status === 'pass' ? '✅' : verdict.status === 'fail' ? '❌' : '⏳';

    // Header showing current execution ID
    let headerHtml = `
        <div class="graph-header">
            <div class="graph-header-id">
                <span class="graph-header-label">Execution:</span>
                <span class="graph-header-value" title="${graph.execution_id}">${graph.execution_id}</span>
            </div>
            <div class="graph-header-stats">
                ${verdictIcon} ${verdict.status?.toUpperCase() || 'UNKNOWN'} · 
                ${graph.node_count || graph.nodes.length} nodes · 
                ${graph.total_latency_ms}ms
            </div>
        </div>
    `;

    // Calculate failed nodes and tainted (downstream) nodes
    const failedNodeIds = new Set(graph.nodes.filter(n => n.verdict_status === 'fail').map(n => n.node_id));
    const taintedNodeIds = new Set();

    // BFS to find tainted nodes
    failedNodeIds.forEach(failedId => {
        graph.edges.forEach(edge => {
            if (failedNodeIds.has(edge.from_node) || taintedNodeIds.has(edge.from_node)) {
                taintedNodeIds.add(edge.to_node);
            }
        });
    });

    // Phase 21: Calculate max latency for time scaling
    const maxLatency = Math.max(...graph.nodes.map(n => n.latency_ms), 1);
    const totalLatency = graph.total_latency_ms || 1;

    // Phase 20: Render hierarchical stages
    let html = headerHtml + '<div class="graph-hierarchy">';

    // Build node lookup
    const nodeMap = {};
    graph.nodes.forEach(n => nodeMap[n.node_id] = n);

    // Check if we have stages
    const hasStages = graph.stages && graph.stages.length > 0;

    if (hasStages) {
        // Render by stages (hierarchical)
        graph.stages.forEach((stage, stageIndex) => {
            const stageNodes = stage.node_ids.map(id => nodeMap[id]).filter(Boolean);
            const hasFailure = stage.has_failure;
            const stageClass = hasFailure ? 'has-failure' : '';

            html += `
                <div class="graph-stage ${stageClass}" data-stage="${stage.stage_id}">
                    <div class="stage-header" onclick="toggleStage('${stage.stage_id}')">
                        <span class="stage-toggle">▼</span>
                        <span class="stage-name">${escapeHtml(stage.name)}</span>
                        <span class="stage-meta">${stage.node_count} nodes · ${stage.total_latency_ms}ms</span>
                        ${hasFailure ? '<span class="stage-failure">❌</span>' : '<span class="stage-pass">✅</span>'}
                    </div>
                    <div class="stage-nodes" id="stage-${stage.stage_id}">
            `;

            // Render nodes in this stage
            stageNodes.forEach((node, nodeIndex) => {
                html += renderGraphNode(node, nodeIndex, stageNodes.length, failedNodeIds, taintedNodeIds, maxLatency);
            });

            html += `
                    </div>
                </div>
            `;

            // Add stage connector
            if (stageIndex < graph.stages.length - 1) {
                html += '<div class="stage-connector">↓</div>';
            }
        });
    } else {
        // Fallback: Flat node list
        html += '<div class="graph-nodes">';
        graph.nodes.forEach((node, index) => {
            html += renderGraphNode(node, index, graph.nodes.length, failedNodeIds, taintedNodeIds, maxLatency);
        });
        html += '</div>';
    }

    html += '</div>';
    container.innerHTML = html;
}

// Toggle stage collapse/expand
function toggleStage(stageId) {
    const stageNodes = document.getElementById(`stage-${stageId}`);
    const stageEl = stageNodes?.parentElement;
    const toggle = stageEl?.querySelector('.stage-toggle');

    if (stageNodes.classList.contains('collapsed')) {
        stageNodes.classList.remove('collapsed');
        if (toggle) toggle.textContent = '▼';
    } else {
        stageNodes.classList.add('collapsed');
        if (toggle) toggle.textContent = '▶';
    }
}

// Phase 22: Toggle Failure Forensics Mode
function toggleForensicsMode() {
    forensicsMode = !forensicsMode;

    const btn = document.getElementById('forensicsToggle');
    const graphContainer = document.getElementById('graphCanvas');

    if (forensicsMode) {
        btn?.classList.add('active');
        graphContainer?.classList.add('forensics-mode');
    } else {
        btn?.classList.remove('active');
        graphContainer?.classList.remove('forensics-mode');
    }

    // Re-render if we have a graph
    if (currentGraph) {
        renderGraph(currentGraph);
    }
}

// Render a single graph node
function renderGraphNode(node, index, total, failedNodeIds, taintedNodeIds, maxLatency = 1000) {
    // Role icons for Phase 19
    const roleIcons = {
        'input': '📥',
        'transform': '🔄',
        'llm': '🤖',
        'tool': '🔧',
        'validation': '✓',
        'output': '📤'
    };

    let statusClass = 'pending';
    let statusIcon = '⏳';

    if (node.verdict_status === 'fail') {
        statusClass = 'fail';
        statusIcon = '❌';
    } else if (node.verdict_status === 'pass') {
        statusClass = 'pass';
        statusIcon = '✅';
    }

    // Phase 26: Golden Reference (BLESSED) styling
    // If blessed, override status class to show golden style
    if (node.blessed) {
        statusClass = 'blessed';
        statusIcon = '⭐';
    }

    // Check if tainted
    const isFailed = failedNodeIds.has(node.node_id);
    const isTainted = taintedNodeIds.has(node.node_id);
    const taintedClass = isTainted ? 'tainted' : '';

    // Phase 19: Use semantic labels
    const role = node.role || 'llm';
    const roleIcon = roleIcons[role] || '🤖';
    const displayLabel = node.human_label || node.label || node.model || 'LLM Call';
    const description = node.description || `${role.toUpperCase()} node`;

    // Phase 21: Time visualization
    // Calculate width percentage based on latency (min 50%, max 100%)
    const latencyRatio = node.latency_ms / maxLatency;
    const widthPercent = 50 + (latencyRatio * 50); // 50% to 100%

    // Heatmap color based on latency (green -> yellow -> red)
    const heatmapColor = getLatencyHeatmapColor(latencyRatio);

    // Is this on critical path? (top 30% latency = bottleneck)
    const isBottleneck = latencyRatio > 0.7;
    const bottleneckClass = isBottleneck ? 'bottleneck' : '';

    // Phase 22: Forensics mode - fade irrelevant nodes
    const isRelevant = isFailed || isTainted;
    const forensicsClass = forensicsMode && !isRelevant ? 'forensics-faded' : '';
    const rootCauseClass = forensicsMode && isFailed && !isTainted ? 'root-cause-pulse' : '';

    let html = `
        <div class="graph-node ${statusClass} ${taintedClass} ${bottleneckClass} ${forensicsClass} ${rootCauseClass}" 
             data-node="${node.node_id}" 
             style="width: ${widthPercent}%; --latency-color: ${heatmapColor};">
            <div class="node-header">
                <span class="node-status">${statusIcon}</span>
                <span class="node-role-icon" title="${role.toUpperCase()}">${roleIcon}</span>
                <span class="node-label">${escapeHtml(displayLabel)}</span>
                ${isTainted ? '<span class="taint-badge">⚠️ TAINTED</span>' : ''}
                ${isBottleneck ? '<span class="bottleneck-badge">🔥 SLOW</span>' : ''}
            </div>
            <div class="node-meta">
                <span class="node-description">${escapeHtml(description)}</span>
                <span class="node-latency">${node.latency_ms}ms</span>
            </div>
            <div class="node-time-bar" style="width: ${latencyRatio * 100}%; background: ${heatmapColor};"></div>
        </div>
    `;

    // Add edge arrow if not last node
    if (index < total - 1) {
        html += '<div class="graph-edge-arrow">↓</div>';
    }

    return html;
}

// Phase 21: Get heatmap color based on latency ratio
function getLatencyHeatmapColor(ratio) {
    // Green (fast) -> Yellow (medium) -> Red (slow)
    if (ratio < 0.3) {
        // Green: #10b981
        return '#10b981';
    } else if (ratio < 0.6) {
        // Yellow: #f59e0b
        return '#f59e0b';
    } else {
        // Red: #ef4444
        return '#ef4444';
    }
}

/**
 * Format timestamp short
 */
function formatTimeShort(timestamp) {
    if (!timestamp) return '';
    const date = new Date(timestamp);
    const now = new Date();
    const diff = now - date;

    if (diff < 60000) return 'Just now';
    if (diff < 86400000) return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
    return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}
