"""
Cascade Live Server - Interactive Investigation Board.

Real-time visualization with deep-dive capabilities.
Features:
- Live Event Feed (Left)
- Interactive Causation Spiderweb (Center)
- Deep Forensic Detail (Right)
- Speculative Reasoning Engine
"""

import asyncio
import json
import webbrowser
from pathlib import Path
from typing import Set, Dict
import http.server
import socketserver
import threading
from queue import Queue
import time

# Shared event queue
event_queue = Queue()

HTML_TEMPLATE = '''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CASCADE // INVESTIGATION</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;600;700;800;900&display=swap');
        
        :root {
            --bg: #050505;
            --panel: #0a0a0a;
            --border: #1a1a1a;
            --border-highlight: #333;
            --text: #e0e0e0;
            --muted: #606060;
            --active: #00ff88;
            --critical: #ff0040;
            --warning: #ffb700;
            --info: #00a8ff;
            --speculative: #8b5cf6;
        }
        
        * { margin: 0; padding: 0; box-sizing: border-box; }
        
        body {
            font-family: 'JetBrains Mono', monospace;
            background: var(--bg);
            color: var(--text);
            height: 100vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        
        /* HEADER */
        .header {
            height: 48px;
            background: var(--panel);
            border-bottom: 1px solid var(--border);
            display: flex;
            align-items: center;
            padding: 0 20px;
            justify-content: space-between;
            z-index: 10;
        }
        
        .logo {
            font-family: 'Inter', sans-serif;
            font-weight: 900;
            font-size: 16px;
            letter-spacing: -0.5px;
        }
        
        .status {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 10px;
            letter-spacing: 1px;
            color: var(--muted);
        }
        .status.active { color: var(--active); }
        .status-dot { width: 6px; height: 6px; background: currentColor; border-radius: 50%; }
        .active .status-dot { box-shadow: 0 0 8px var(--active); animation: pulse 2s infinite; }
        
        @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }

        /* MAIN LAYOUT */
        .workspace {
            flex: 1;
            display: grid;
            grid-template-columns: 300px 1fr 350px;
            overflow: hidden;
        }
        
        /* FEED (Left) */
        .feed-panel {
            border-right: 1px solid var(--border);
            display: flex;
            flex-direction: column;
            background: rgba(10, 10, 10, 0.5);
        }
        
        .panel-header {
            padding: 12px 16px;
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 1px;
            color: var(--muted);
            text-transform: uppercase;
            border-bottom: 1px solid var(--border);
            display: flex;
            justify-content: space-between;
        }
        
        .event-list {
            flex: 1;
            overflow-y: auto;
            overflow-x: hidden;
        }
        
        .event-item {
            padding: 10px 16px;
            border-bottom: 1px solid var(--border);
            cursor: pointer;
            transition: all 0.2s;
            border-left: 2px solid transparent;
            position: relative;
        }
        
        .event-item:hover { background: var(--border); }
        .event-item.selected { background: #151515; border-left-color: var(--info); }
        .event-item.new { animation: flash 1s; }
        
        @keyframes flash { 0% { background: rgba(0, 255, 136, 0.2); } 100% { background: transparent; } }
        
        .event-meta { display: flex; justify-content: space-between; margin-bottom: 4px; }
        .event-time { font-size: 10px; color: var(--muted); }
        .event-type { font-size: 11px; font-weight: 600; color: var(--text); }
        
        .event-preview { 
            font-size: 10px; color: var(--muted); 
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis; 
        }
        
        /* CANVAS (Center) */
        .investigation-panel {
            position: relative;
            background: 
                radial-gradient(circle at 50% 50%, #111 0%, #050505 100%),
                linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px);
            background-size: 100% 100%, 40px 40px, 40px 40px;
            overflow: hidden;
            cursor: grab;
        }
        
        .canvas-svg {
            width: 100%;
            height: 100%;
        }
        
        /* DETAILS (Right) */
        .detail-panel {
            border-left: 1px solid var(--border);
            display: flex;
            flex-direction: column;
            background: var(--panel);
        }
        
        .detail-content {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
        }
        
        .detail-section { margin-bottom: 24px; }
        .detail-label { 
            font-size: 9px; color: var(--muted); letter-spacing: 1.5px; 
            text-transform: uppercase; margin-bottom: 8px; font-weight: 700;
        }
        
        .data-block {
            background: #000;
            border: 1px solid var(--border);
            padding: 12px;
            font-family: 'JetBrains Mono', monospace;
            font-size: 11px;
            color: var(--info);
            white-space: pre-wrap;
            overflow-x: auto;
        }
        
        .speculation-box {
            border: 1px dashed var(--speculative);
            background: rgba(139, 92, 246, 0.05);
            padding: 12px;
            margin-top: 8px;
        }
        
        .spec-header { 
            color: var(--speculative); font-size: 10px; font-weight: 700; 
            margin-bottom: 8px; display: flex; align-items: center; gap: 6px; 
        }
        
        /* MOTTO FOOTER */
        .footer {
            height: 32px;
            background: var(--panel);
            border-top: 1px solid var(--border);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 20;
        }
        .motto { font-size: 10px; font-style: italic; color: #444; }

        /* SVG STYLES */
        /* The Tunnel/Bridge Aesthetic */
        .link-group { pointer-events: none; }
        
        .link-tunnel { 
            stroke: #1a1a1a; 
            stroke-width: 6px; 
            fill: none; 
            opacity: 0.5;
        }
        
        .link-core { 
            stroke: var(--border-highlight); 
            stroke-width: 1px; 
            fill: none; 
            stroke-dasharray: 4; 
        }
        
        .active-flow .link-tunnel { stroke: rgba(0, 255, 136, 0.1); }
        .active-flow .link-core { 
            stroke: var(--active); 
            stroke-width: 1.5px;
            animation: flow 1s linear infinite; 
        }
        
        @keyframes flow { to { stroke-dashoffset: -8; } }
        
        .node circle { 
            fill: #000; 
            stroke: var(--muted); 
            stroke-width: 1px; 
            transition: all 0.3s; 
            r: 4;
        }
        
        .node.selected circle { 
            stroke: var(--info); 
            stroke-width: 2px; 
            fill: #050505; 
            r: 24;
            filter: drop-shadow(0 0 8px rgba(0, 168, 255, 0.3));
        }
        
        /* "Seeing Stone" Lens Effect for Center Node */
        .node.selected::after {
            content: '';
            position: absolute;
            top: -50%; left: -50%; width: 200%; height: 200%;
            background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
            border-radius: 50%;
        }

        .node text { 
            font-size: 10px; 
            fill: var(--muted); 
            font-family: 'JetBrains Mono', monospace; 
            pointer-events: none; 
            text-anchor: middle;
            dominant-baseline: middle;
            opacity: 0;
            transition: opacity 0.2s;
        }
        
        .node:hover text, .node.selected text { opacity: 1; }
        .node.selected text { fill: var(--text); font-weight: 700; font-size: 12px; dy: 0; }
        
        .node.error circle { stroke: var(--critical); }

    </style>
</head>
<body>
    <header class="header">
        <div class="logo">CASCADE // THE SIGHT</div>
        <div class="status" id="status">
            <div class="status-dot"></div>
            <span id="status-text">OFFLINE</span>
        </div>
    </header>
    
    <div class="workspace">
        <div class="feed-panel">
            <div class="panel-header">
                <span>Incoming Stream</span>
                <span id="event-count">0</span>
            </div>
            <div class="event-list" id="event-list"></div>
        </div>
        
        <div class="investigation-panel" id="canvas-container">
            <svg class="canvas-svg" id="canvas">
                <defs>
                    <filter id="glow">
                        <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
                        <feMerge>
                            <feMergeNode in="coloredBlur"/>
                            <feMergeNode in="SourceGraphic"/>
                        </feMerge>
                    </filter>
                </defs>
                <g id="links-group"></g>
                <g id="nodes-group"></g>
            </svg>
        </div>
        
        <div class="detail-panel">
            <div class="panel-header">Analysis & Speculation</div>
            <div class="detail-content" id="detail-view">
                <div style="color: var(--muted); text-align: center; margin-top: 100px;">
                    USE THE SIGHT<br>REVEAL CAUSATION
                </div>
            </div>
        </div>
    </div>
    
    <footer class="footer">
        <div class="motto">"even still, i grow, and yet, I grow still"</div>
    </footer>

    <script>
        // --- DATA & STATE ---
        const events = new Map();
        const links = [];
        let selectedId = null;
        
        // --- UI REFS ---
        const eventList = document.getElementById('event-list');
        const countEl = document.getElementById('event-count');
        const detailView = document.getElementById('detail-view');
        
        // --- SVG GRAPH ---
        const svg = document.getElementById('canvas');
        const nodesGroup = document.getElementById('nodes-group');
        const linksGroup = document.getElementById('links-group');
        let width = svg.clientWidth;
        let height = svg.clientHeight;
        
        window.addEventListener('resize', () => {
            width = svg.clientWidth;
            height = svg.clientHeight;
            renderGraph();
        });

        // --- CONNECTION ---
        const statusEl = document.getElementById('status');
        const statusText = document.getElementById('status-text');
        
        function connect() {
            const source = new EventSource('/events');
            
            source.onopen = () => {
                statusEl.classList.add('active');
                statusText.textContent = 'SYSTEM CONNECTED';
            };
            
            source.onerror = () => {
                statusEl.classList.remove('active');
                statusText.textContent = 'RECONNECTING...';
            };
            
            source.onmessage = (e) => {
                if(e.data.startsWith(':')) return;
                try {
                    const data = JSON.parse(e.data);
                    if(data.event) handleEvent(data.event);
                } catch(e) {}
            };
        }
        
        // --- EVENT HANDLING ---
        function handleEvent(evt) {
            events.set(evt.event_id, evt);
            
            // Add to list
            const el = document.createElement('div');
            el.className = 'event-item new';
            el.dataset.id = evt.event_id;
            el.innerHTML = `
                <div class="event-meta">
                    <span class="event-type">${evt.event_type}</span>
                    <span class="event-time">${new Date(evt.timestamp * 1000).toLocaleTimeString().split(' ')[0]}</span>
                </div>
                <div class="event-preview">${evt.component}</div>
            `;
            el.onclick = () => selectEvent(evt.event_id);
            
            eventList.insertBefore(el, eventList.firstChild);
            
            // Dynamic list management - virtualize if needed for performance
            // No hard limit - use configurable max via window.CASCADE_MAX_LIST_EVENTS
            const maxList = window.CASCADE_MAX_LIST_EVENTS || Infinity;
            if(maxList !== Infinity && eventList.children.length > maxList) {
                eventList.lastChild.remove();
            }
            
            // Update count
            countEl.textContent = events.size;
            
            // Auto-select if first
            if(!selectedId) selectEvent(evt.event_id);
            
            // If we are looking at something related, update graph
            if(selectedId) renderGraph(); 
        }
        
        // --- SELECTION & GRAPH LOGIC ---
        function selectEvent(id) {
            selectedId = id;
            
            // Highlight list
            document.querySelectorAll('.event-item').forEach(el => el.classList.remove('selected'));
            const item = document.querySelector(`.event-item[data-id="${id}"]`);
            if(item) item.classList.add('selected');
            
            renderDetail(id);
            renderGraph();
        }
        
        function renderGraph() {
            if(!selectedId) return;
            
            const centerEvt = events.get(selectedId);
            if(!centerEvt) return;
            
            // Layout: Center node is the "Looking Glass"
            // Tunnels extend to past (left) and future (right, speculative)
            
            const allEvents = Array.from(events.values()).sort((a,b) => a.timestamp - b.timestamp);
            const idx = allEvents.findIndex(e => e.event_id === selectedId);
            
            const nodes = [];
            const links = [];
            
            // Center
            nodes.push({ ...centerEvt, x: width/2, y: height/2, role: 'center' });
            
            // Past Tunnels (curved paths?)
            for(let i=1; i<=3; i++) {
                if(idx - i >= 0) {
                    const evt = allEvents[idx - i];
                    const node = { 
                        ...evt, 
                        x: width/2 - (i*120), 
                        y: height/2 + (Math.sin(i)*50), 
                        role: 'past' 
                    };
                    nodes.push(node);
                    links.push({ source: node, target: nodes[0], type: 'past' });
                }
            }
            
            // Future Tunnels (Speculative)
            for(let i=1; i<=3; i++) {
                if(idx + i < allEvents.length) {
                    const evt = allEvents[idx + i];
                    const node = { 
                        ...evt, 
                        x: width/2 + (i*120), 
                        y: height/2 + (Math.cos(i)*50), 
                        role: 'future' 
                    };
                    nodes.push(node);
                    links.push({ source: nodes[0], target: node, type: 'future' });
                }
            }
            
            // DRAW LINKS (TUNNELS)
            linksGroup.innerHTML = links.map(l => {
                // Calculate curve control point for tunnel effect
                const mx = (l.source.x + l.target.x) / 2;
                const my = (l.source.y + l.target.y) / 2 + 20; // slight drape
                const d = `M ${l.source.x} ${l.source.y} Q ${mx} ${my} ${l.target.x} ${l.target.y}`;
                
                return `
                    <g class="link-group ${l.type === 'future' ? 'active-flow' : ''}">
                        <path class="link-tunnel" d="${d}" />
                        <path class="link-core" d="${d}" />
                    </g>
                `;
            }).join('');
            
            // DRAW NODES
            nodesGroup.innerHTML = nodes.map(n => `
                <g class="node ${n.event_id === selectedId ? 'selected' : ''} ${n.event_type.includes('error') ? 'error' : ''}"
                   onclick="selectEvent('${n.event_id}')"
                   transform="translate(${n.x}, ${n.y})">
                    <circle></circle>
                    <text dy="30">${n.event_type}</text>
                </g>
            `).join('');
        }
        
        function renderDetail(id) {
            const evt = events.get(id);
            if(!evt) return;
            
            detailView.innerHTML = `
                <div class="detail-section">
                    <div class="detail-label">Event Identity</div>
                    <div style="font-size: 14px; font-weight: 700; color: var(--text)">${evt.event_type}</div>
                    <div style="color: var(--muted); font-size: 11px;">${evt.component} | ${evt.event_id}</div>
                </div>
                
                <div class="detail-section">
                    <div class="detail-label">Payload Data</div>
                    <div class="data-block">${JSON.stringify(evt.data, null, 2)}</div>
                </div>
                
                <div class="speculation-box">
                    <div class="spec-header">
                        <span>✦</span> SPECULATIVE REASONING
                    </div>
                    <div style="font-size: 11px; color: var(--text); line-height: 1.5;">
                        Analyzing causal vector...<br>
                        <span style="color: var(--muted)">> Probability of cascade: LO</span><br>
                        <span style="color: var(--muted)">> System stability: NOMINAL</span>
                    </div>
                </div>
            `;
        }
        
        connect();
        
    </script>
</body>
</html>
'''

class CascadeLiveServer:
    """Reusable Live Server Module"""
    def __init__(self, port=8888):
        self.port = port

    def start(self, open_browser=True):
        socketserver.TCPServer.allow_reuse_address = True
        
        instance = self # Closure hack
        class Handler(http.server.BaseHTTPRequestHandler):
            def do_GET(self):
                if self.path == '/events':
                    self.send_response(200)
                    self.send_header('Content-type', 'text/event-stream')
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    while True:
                        try:
                            # Check global queue for demo simplicity
                            while not event_queue.empty():
                                data = event_queue.get_nowait()
                                self.wfile.write(f"data: {json.dumps(data)}\n\n".encode())
                                self.wfile.flush()
                                print(f"[SSE] Dispatched event {data.get('event', {}).get('event_id')}")
                            self.wfile.write(b": keepalive\n\n")
                            self.wfile.flush()
                            time.sleep(0.5)
                        except (ConnectionAbortedError, BrokenPipeError): return
                        except Exception: return
                        
                else:
                    self.send_response(200)
                    self.send_header('Content-type', 'text/html')
                    self.end_headers()
                    self.wfile.write(HTML_TEMPLATE.encode())
            
            def log_message(self, fmt, *args): pass

        print(f"[CASCADE] Live Interface on http://localhost:{self.port}")
        threading.Thread(target=lambda: socketserver.TCPServer(("", self.port), Handler).serve_forever(), daemon=True).start()
        
        if open_browser:
            webbrowser.open(f"http://localhost:{self.port}")
