"""Agent Dashboard — Real PraisonAI Integration.

Unlike 12-agent-dashboard (mock data), this example runs REAL
PraisonAI agents. You can chat with them from the browser and
see live metrics (conversations, tokens, response times).

Requires:
    pip install praisonaiui praisonaiagents
    export OPENAI_API_KEY=sk-...

Run:
    PYTHONPATH=src python examples/python/13-real-dashboard/app.py
    # Dashboard at http://localhost:8082
"""

import os
import sys
import time
import json
import asyncio
import threading
from collections import defaultdict

import uvicorn
from starlette.responses import HTMLResponse, JSONResponse
from starlette.routing import Route

from praisonaiui.server import create_app

try:
    from praisonaiagents import Agent
except ImportError:
    raise ImportError("praisonaiagents required: pip install praisonaiagents")


# ── Real Agent Registry ─────────────────────────────────────────────

class AgentRegistry:
    """Tracks real agents, conversations, and metrics."""

    def __init__(self):
        self.agents = {}
        self.conversations = []  # {id, agent, user_msg, agent_msg, tokens, latency_ms, ts}
        self.stats = defaultdict(lambda: {"calls": 0, "tokens": 0, "errors": 0, "avg_ms": 0})
        self._lock = threading.Lock()

    def register(self, agent_id: str, agent: Agent, description: str = ""):
        self.agents[agent_id] = {
            "agent": agent,
            "id": agent_id,
            "name": agent.name,
            "description": description,
            "instructions": agent.instructions[:100] + "..." if len(agent.instructions) > 100 else agent.instructions,
            "created_at": time.time(),
            "status": "ready",
        }

    def chat(self, agent_id: str, message: str) -> dict:
        """Run a real agent.chat() and record metrics."""
        info = self.agents.get(agent_id)
        if not info:
            return {"error": f"Agent '{agent_id}' not found"}

        agent = info["agent"]
        info["status"] = "busy"
        start = time.time()

        try:
            response = agent.chat(message)
            latency_ms = round((time.time() - start) * 1000)
            response_str = str(response)
            est_tokens = len(response_str.split())

            conv = {
                "id": len(self.conversations) + 1,
                "agent": agent_id,
                "agent_name": info["name"],
                "user_msg": message,
                "agent_msg": response_str[:500],
                "tokens": est_tokens,
                "latency_ms": latency_ms,
                "ts": time.time(),
            }

            with self._lock:
                self.conversations.append(conv)
                s = self.stats[agent_id]
                s["calls"] += 1
                s["tokens"] += est_tokens
                total_ms = s["avg_ms"] * (s["calls"] - 1) + latency_ms
                s["avg_ms"] = round(total_ms / s["calls"])

            info["status"] = "ready"
            return conv

        except Exception as e:
            info["status"] = "error"
            with self._lock:
                self.stats[agent_id]["errors"] += 1
            return {"error": str(e), "agent": agent_id}

    def get_agents_summary(self):
        return [
            {
                "id": k,
                "name": v["name"],
                "description": v["description"],
                "instructions": v["instructions"],
                "status": v["status"],
                "stats": dict(self.stats.get(k, {})),
            }
            for k, v in self.agents.items()
        ]

    def get_conversations(self, limit=50):
        with self._lock:
            return list(reversed(self.conversations[-limit:]))

    def get_overview(self):
        with self._lock:
            total_calls = sum(s["calls"] for s in self.stats.values())
            total_tokens = sum(s["tokens"] for s in self.stats.values())
            total_errors = sum(s["errors"] for s in self.stats.values())
            avg_latency = 0
            if total_calls > 0:
                avg_latency = round(sum(s["avg_ms"] * s["calls"] for s in self.stats.values()) / total_calls)
            return {
                "agents": len(self.agents),
                "total_conversations": len(self.conversations),
                "total_tokens": total_tokens,
                "total_errors": total_errors,
                "avg_latency_ms": avg_latency,
            }


# ── Create real agents ───────────────────────────────────────────────

registry = AgentRegistry()

api_key = os.getenv("OPENAI_API_KEY", "")
if not api_key:
    print("⚠️  OPENAI_API_KEY not set — agents will fail on first call")

registry.register("assistant", Agent(
    name="General Assistant",
    instructions="You are a helpful, concise assistant. Answer clearly using markdown.",
    llm="gpt-4o-mini",
), description="General-purpose Q&A agent")

registry.register("code-reviewer", Agent(
    name="Code Reviewer",
    instructions="You are a senior code reviewer. Review code for bugs, style, and performance. Be constructive and specific.",
    llm="gpt-4o-mini",
), description="Reviews code for bugs and style")

registry.register("summarizer", Agent(
    name="Summarizer",
    instructions="You are a summarization expert. Summarize text into clear, concise bullet points. Keep it under 5 bullets.",
    llm="gpt-4o-mini",
), description="Summarizes text into bullet points")


# ── API endpoints ────────────────────────────────────────────────────

async def api_overview(request):
    return JSONResponse(registry.get_overview())

async def api_agents(request):
    return JSONResponse({"agents": registry.get_agents_summary(), "count": len(registry.agents)})

async def api_conversations(request):
    return JSONResponse({"conversations": registry.get_conversations(), "count": len(registry.conversations)})

async def api_chat(request):
    body = await request.json()
    agent_id = body.get("agent_id", "assistant")
    message = body.get("message", "")
    if not message:
        return JSONResponse({"error": "message required"}, status_code=400)
    # Run in thread to not block
    result = await asyncio.to_thread(registry.chat, agent_id, message)
    return JSONResponse(result)


# ── HTML Dashboard ───────────────────────────────────────────────────

DASHBOARD_HTML = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Dashboard — Live</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
    --bg: #0a0a12; --card: #12121f; --hover: #1a1a2e; --border: #2a2a4a;
    --text: #e0e0e8; --dim: #94a3b8;
    --purple: #7c3aed; --cyan: #06b6d4; --green: #22c55e; --amber: #f59e0b; --red: #ef4444;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); display: flex; min-height: 100vh; }

/* Sidebar */
.sidebar { width: 240px; background: var(--card); border-right: 1px solid var(--border);
           position: fixed; height: 100vh; display: flex; flex-direction: column; }
.sidebar h1 { font-size: 1.1rem; padding: 1.25rem; border-bottom: 1px solid var(--border);
              background: linear-gradient(90deg, var(--purple), var(--cyan));
              -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.sidebar p { font-size: 0.65rem; color: var(--dim); padding: 0 1.25rem; margin-top: -0.75rem; margin-bottom: 0.5rem; }
.nav-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 1rem;
            cursor: pointer; font-size: 0.85rem; color: var(--dim); border-left: 3px solid transparent; transition: all 0.2s; }
.nav-item:hover { background: var(--hover); color: var(--text); }
.nav-item.active { background: var(--hover); color: var(--purple); border-left-color: var(--purple); }
.nav-group { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--dim);
             padding: 0.75rem 1rem 0.25rem; font-weight: 600; }
.live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); margin-left: auto;
            animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Main */
.main { margin-left: 240px; flex: 1; padding: 1.5rem; }
.page { display: none; } .page.active { display: block; animation: fadeIn 0.25s ease; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.page-title { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.25rem; }
.page-sub { color: var(--dim); font-size: 0.9rem; margin-bottom: 1.5rem; }

/* Stats */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
.stat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 1.25rem; }
.stat:hover { border-color: var(--purple); }
.stat-label { font-size: 0.7rem; color: var(--dim); text-transform: uppercase; letter-spacing: 0.05em; }
.stat-value { font-size: 1.75rem; font-weight: 700; margin-top: 0.25rem;
              background: linear-gradient(90deg, var(--purple), var(--cyan));
              -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.stat-sub { font-size: 0.7rem; color: var(--dim); margin-top: 0.25rem; }

/* Table */
table { width: 100%; background: var(--card); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
th { text-align: left; padding: 0.7rem 1rem; font-size: 0.7rem; text-transform: uppercase;
     letter-spacing: 0.05em; color: var(--dim); background: var(--hover); border-bottom: 1px solid var(--border); }
td { padding: 0.7rem 1rem; font-size: 0.85rem; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(124, 58, 237, 0.04); }

/* Badges */
.badge { padding: 2px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; }
.b-ready { background: rgba(34,197,94,0.15); color: var(--green); }
.b-busy { background: rgba(245,158,11,0.15); color: var(--amber); }
.b-error { background: rgba(239,68,68,0.15); color: var(--red); }

/* Chat */
.chat-area { display: flex; gap: 1rem; height: calc(100vh - 180px); }
.chat-agents { width: 200px; flex-shrink: 0; }
.agent-btn { width: 100%; padding: 0.75rem; background: var(--card); border: 1px solid var(--border);
             border-radius: 8px; color: var(--text); cursor: pointer; text-align: left; margin-bottom: 0.5rem;
             font-size: 0.85rem; transition: all 0.2s; }
.agent-btn:hover, .agent-btn.selected { border-color: var(--purple); background: var(--hover); }
.agent-btn .agent-desc { font-size: 0.7rem; color: var(--dim); margin-top: 2px; }
.chat-main { flex: 1; display: flex; flex-direction: column; }
.chat-messages { flex: 1; background: var(--card); border: 1px solid var(--border); border-radius: 10px;
                 padding: 1rem; overflow-y: auto; margin-bottom: 1rem; }
.msg { margin-bottom: 1rem; padding: 0.75rem; border-radius: 8px; max-width: 85%; }
.msg-user { background: var(--purple); color: white; margin-left: auto; }
.msg-agent { background: var(--hover); border: 1px solid var(--border); }
.msg-meta { font-size: 0.65rem; color: var(--dim); margin-top: 4px; }
.chat-input { display: flex; gap: 0.5rem; }
.chat-input input { flex: 1; padding: 0.75rem 1rem; background: var(--card); border: 1px solid var(--border);
                    border-radius: 8px; color: var(--text); font-size: 0.9rem; outline: none; }
.chat-input input:focus { border-color: var(--purple); }
.chat-input button { padding: 0.75rem 1.5rem; background: var(--purple); color: white; border: none;
                     border-radius: 8px; cursor: pointer; font-weight: 600; transition: background 0.2s; }
.chat-input button:hover { background: #6d28d9; }
.chat-input button:disabled { background: #333; cursor: not-allowed; }
.typing { color: var(--dim); font-size: 0.85rem; padding: 0.5rem; }
</style>
</head>
<body>
<aside class="sidebar">
    <h1>🤖 Agent Dashboard</h1>
    <p>Live PraisonAI Integration</p>
    <div class="nav-group">Overview</div>
    <div class="nav-item active" data-page="overview">📊 Overview <div class="live-dot"></div></div>
    <div class="nav-item" data-page="agents">🧠 Agents</div>
    <div class="nav-group">Interact</div>
    <div class="nav-item" data-page="chat">💬 Chat</div>
    <div class="nav-item" data-page="history">📜 History</div>
</aside>
<main class="main">
    <!-- OVERVIEW -->
    <div class="page active" id="page-overview">
        <div class="page-title">Overview</div>
        <div class="page-sub">Live metrics from real PraisonAI agents</div>
        <div class="stats" id="overview-stats"></div>
        <h3 style="margin-bottom:0.75rem;font-size:0.9rem;color:var(--dim)">Recent Conversations</h3>
        <table id="recent-table"><thead><tr>
            <th>Agent</th><th>Message</th><th>Tokens</th><th>Latency</th><th>Time</th>
        </tr></thead><tbody></tbody></table>
    </div>

    <!-- AGENTS -->
    <div class="page" id="page-agents">
        <div class="page-title">Agents</div>
        <div class="page-sub">Registered PraisonAI agents</div>
        <table id="agents-table"><thead><tr>
            <th>Name</th><th>ID</th><th>Description</th><th>Status</th><th>Calls</th><th>Tokens</th><th>Avg Latency</th>
        </tr></thead><tbody></tbody></table>
    </div>

    <!-- CHAT -->
    <div class="page" id="page-chat">
        <div class="page-title">Chat with Agents</div>
        <div class="page-sub">Send real messages to PraisonAI agents</div>
        <div class="chat-area">
            <div class="chat-agents" id="agent-buttons"></div>
            <div class="chat-main">
                <div class="chat-messages" id="chat-messages"></div>
                <div class="chat-input">
                    <input type="text" id="chat-input" placeholder="Type a message..." />
                    <button id="chat-send" onclick="sendMessage()">Send</button>
                </div>
            </div>
        </div>
    </div>

    <!-- HISTORY -->
    <div class="page" id="page-history">
        <div class="page-title">Conversation History</div>
        <div class="page-sub">All conversations with real agents</div>
        <table id="history-table"><thead><tr>
            <th>#</th><th>Agent</th><th>User Message</th><th>Agent Response</th><th>Tokens</th><th>Latency</th>
        </tr></thead><tbody></tbody></table>
    </div>
</main>
<script>
// Navigation
document.querySelectorAll('.nav-item').forEach(item => {
    item.addEventListener('click', () => {
        document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
        document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
        item.classList.add('active');
        document.getElementById('page-' + item.dataset.page).classList.add('active');
    });
});

let selectedAgent = 'assistant';

async function loadData() {
    const [overview, agents, convos] = await Promise.all([
        fetch('/dashboard/api/overview').then(r => r.json()),
        fetch('/dashboard/api/agents').then(r => r.json()),
        fetch('/dashboard/api/conversations').then(r => r.json()),
    ]);

    // Overview stats
    document.getElementById('overview-stats').innerHTML = `
        <div class="stat"><div class="stat-label">Agents</div><div class="stat-value">${overview.agents}</div><div class="stat-sub">Registered & ready</div></div>
        <div class="stat"><div class="stat-label">Conversations</div><div class="stat-value">${overview.total_conversations}</div><div class="stat-sub">Real agent calls</div></div>
        <div class="stat"><div class="stat-label">Tokens</div><div class="stat-value">${overview.total_tokens.toLocaleString()}</div><div class="stat-sub">Estimated output tokens</div></div>
        <div class="stat"><div class="stat-label">Errors</div><div class="stat-value">${overview.total_errors}</div><div class="stat-sub">Failed calls</div></div>
        <div class="stat"><div class="stat-label">Avg Latency</div><div class="stat-value">${overview.avg_latency_ms}ms</div><div class="stat-sub">Response time</div></div>
    `;

    // Recent convos
    const tbody = document.querySelector('#recent-table tbody');
    tbody.innerHTML = convos.conversations.slice(0, 10).map(c => `<tr>
        <td><strong>${c.agent_name}</strong></td>
        <td>${c.user_msg.substring(0, 60)}${c.user_msg.length > 60 ? '...' : ''}</td>
        <td>${c.tokens}</td><td>${c.latency_ms}ms</td>
        <td>${new Date(c.ts * 1000).toLocaleTimeString()}</td>
    </tr>`).join('') || '<tr><td colspan="5" style="text-align:center;color:var(--dim);padding:2rem">No conversations yet — try the Chat tab!</td></tr>';

    // Agents table
    document.querySelector('#agents-table tbody').innerHTML = agents.agents.map(a => `<tr>
        <td><strong>${a.name}</strong></td><td><code>${a.id}</code></td>
        <td>${a.description}</td>
        <td><span class="badge b-${a.status}">${a.status}</span></td>
        <td>${a.stats.calls || 0}</td><td>${(a.stats.tokens || 0).toLocaleString()}</td>
        <td>${a.stats.avg_ms || 0}ms</td>
    </tr>`).join('');

    // Agent buttons for chat
    document.getElementById('agent-buttons').innerHTML = agents.agents.map(a => `
        <button class="agent-btn ${a.id === selectedAgent ? 'selected' : ''}" onclick="selectAgent('${a.id}')">
            <div>${a.name}</div><div class="agent-desc">${a.description}</div>
        </button>
    `).join('');

    // Full history
    document.querySelector('#history-table tbody').innerHTML = convos.conversations.map(c => `<tr>
        <td>${c.id}</td><td><strong>${c.agent_name}</strong></td>
        <td>${c.user_msg.substring(0, 80)}</td>
        <td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${c.agent_msg.substring(0, 100)}</td>
        <td>${c.tokens}</td><td>${c.latency_ms}ms</td>
    </tr>`).join('') || '<tr><td colspan="6" style="text-align:center;color:var(--dim);padding:2rem">No conversations yet</td></tr>';
}

function selectAgent(id) {
    selectedAgent = id;
    document.querySelectorAll('.agent-btn').forEach(b => b.classList.remove('selected'));
    event.currentTarget.classList.add('selected');
}

async function sendMessage() {
    const input = document.getElementById('chat-input');
    const msg = input.value.trim();
    if (!msg) return;

    const messages = document.getElementById('chat-messages');
    messages.innerHTML += `<div class="msg msg-user">${msg}</div>`;
    input.value = '';
    document.getElementById('chat-send').disabled = true;
    messages.innerHTML += '<div class="typing" id="typing">⏳ Agent thinking...</div>';
    messages.scrollTop = messages.scrollHeight;

    try {
        const res = await fetch('/dashboard/api/chat', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({agent_id: selectedAgent, message: msg}),
        });
        const data = await res.json();
        document.getElementById('typing')?.remove();

        if (data.error) {
            messages.innerHTML += `<div class="msg msg-agent" style="border-color:var(--red)">❌ ${data.error}</div>`;
        } else {
            messages.innerHTML += `<div class="msg msg-agent">${data.agent_msg}
                <div class="msg-meta">${data.agent_name} · ${data.tokens} tokens · ${data.latency_ms}ms</div></div>`;
        }
    } catch (e) {
        document.getElementById('typing')?.remove();
        messages.innerHTML += `<div class="msg msg-agent" style="border-color:var(--red)">❌ Network error: ${e.message}</div>`;
    }

    document.getElementById('chat-send').disabled = false;
    messages.scrollTop = messages.scrollHeight;
    loadData(); // Refresh stats
}

document.getElementById('chat-input').addEventListener('keydown', e => { if (e.key === 'Enter') sendMessage(); });

loadData();
setInterval(loadData, 5000);
</script>
</body>
</html>"""


# ── Entry point ──────────────────────────────────────────────────────

if __name__ == "__main__":
    app = create_app()

    app.routes.insert(0, Route("/", lambda r: HTMLResponse(DASHBOARD_HTML), methods=["GET"]))
    app.routes.insert(1, Route("/dashboard/api/overview", api_overview, methods=["GET"]))
    app.routes.insert(2, Route("/dashboard/api/agents", api_agents, methods=["GET"]))
    app.routes.insert(3, Route("/dashboard/api/conversations", api_conversations, methods=["GET"]))
    app.routes.insert(4, Route("/dashboard/api/chat", api_chat, methods=["POST"]))

    print("✅ Real Agent Dashboard at http://localhost:8082")
    print("   3 live PraisonAI agents: assistant, code-reviewer, summarizer")
    print("   Chat with them from the browser!")
    uvicorn.run(app, host="0.0.0.0", port=8082, log_level="info")
