#!/usr/bin/env python3
"""
AIVA Voice Command Bridge - Mobile Dashboard
FastAPI application with optimized database operations and dashboard serving.
"""

import os
import logging
import time
from datetime import datetime, timedelta
from typing import Optional, List, Dict, Any
from contextlib import contextmanager
from functools import lru_cache
import threading
import uuid

from fastapi import FastAPI, HTTPException, Header, Request, Response
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, JSONResponse
from pydantic import BaseModel
import psycopg2
from psycopg2 import pool
from psycopg2.extras import RealDictCursor
import uvicorn

from core.secrets_loader import get_postgres_config

# ============================================================================
# CONFIGURATION
# ============================================================================

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

API_KEY = "AIVA-BRIDGE-SECRET-KEY-2024"

# ============================================================================
# DATABASE CONNECTION POOL (Thread-safe, production-ready)
# ============================================================================

class DatabasePool:
    """Optimized connection pool with health checks and auto-recovery."""
    
    _instance: Optional['DatabasePool'] = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self._pool: Optional[pool.ThreadedConnectionPool] = None
        self._last_health_check = 0
        self._health_status = "unknown"
        
    def initialize(self):
        """Initialize the connection pool."""
        if self._pool is not None:
            return
        try:
            pg_config = get_postgres_config()
            if not pg_config.is_configured:
                raise psycopg2.Error("PostgreSQL is not configured.")

            self._pool = pool.ThreadedConnectionPool(
                minconn=2,
                maxconn=10,
                host=pg_config.host,
                port=pg_config.port,
                user=pg_config.user,
                password=pg_config.password,
                database=pg_config.dbname,
                command_timeout=10
            )
            logger.info("Database connection pool initialized successfully")
        except Exception as e:
            logger.error(f"Failed to initialize database pool: {e}")
            raise
    
    @contextmanager
    def get_connection(self):
        """Context manager for database connections with auto-retry."""
        if self._pool is None:
            self.initialize()
        
        conn = None
        retries = 2
        for attempt in range(retries):
            try:
                conn = self._pool.getconn()
                if conn.closed:
                    conn = self._pool.getconn()
                yield conn
                return
            except Exception as e:
                logger.warning(f"Database connection attempt {attempt + 1} failed: {e}")
                if conn and not conn.closed:
                    try:
                        conn.rollback()
                    except:
                        pass
                if attempt == retries - 1:
                    raise
                time.sleep(0.1)
            finally:
                if conn:
                    self._pool.putconn(conn)
    
    def health_check(self) -> Dict[str, Any]:
        """Perform health check on database connection."""
        try:
            with self.get_connection() as conn:
                with conn.cursor() as cur:
                    cur.execute("SELECT 1")
                    cur.fetchone()
                    self._health_status = "healthy"
                    return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
        except Exception as e:
            self._health_status = "unhealthy"
            return {"status": "unhealthy", "error": str(e), "timestamp": datetime.utcnow().isoformat()}
    
    @property
    def status(self) -> str:
        return self._health_status

db_pool = DatabasePool()

# ============================================================================
# FASTAPI APPLICATION
# ============================================================================

app = FastAPI(
    title="AIVA Voice Command Bridge",
    version="2.0.0",
    description="Genesis Voice Command Bridge API with Mobile Dashboard"
)

# In-memory state (production would use Redis/stateful storage)
class BridgeState:
    """In-memory bridge state with thread-safe operations."""
    _instance: Optional['BridgeState'] = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self._start_time = datetime.utcnow()
        self._api_uptime = time.time()
        self._poller_active = True
        self._ws_clients = 0
        self._request_count = 0
        self._lock = threading.Lock()
        
    def get_uptime(self) -> float:
        return time.time() - self._api_uptime
    
    def increment_requests(self):
        with self._lock:
            self._request_count += 1
    
    def get_stats(self) -> Dict[str, Any]:
        with self._lock:
            return {
                "start_time": self._start_time.isoformat(),
                "uptime_seconds": self.get_uptime(),
                "request_count": self._request_count,
                "poller_active": self._poller_active,
                "ws_clients": self._ws_clients
            }

bridge_state = BridgeState()

# ============================================================================
# DATABASE SCHEMA SETUP
# ============================================================================

def setup_schema():
    """Create schema and tables if they don't exist."""
    schema_sql = """
    CREATE SCHEMA IF NOT EXISTS genesis_bridge;
    
    CREATE TABLE IF NOT EXISTS genesis_bridge.directives (
        id SERIAL PRIMARY KEY,
        uuid UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
        command_text TEXT NOT NULL,
        priority INTEGER DEFAULT 5 CHECK (priority BETWEEN 1 AND 10),
        status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed', 'cancelled')),
        source VARCHAR(50) DEFAULT 'voice',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        completed_at TIMESTAMP NULL,
        response_data JSONB NULL,
        error_message TEXT NULL,
        metadata JSONB DEFAULT '{}'::jsonb
    );
    
    CREATE TABLE IF NOT EXISTS genesis_bridge.command_history (
        id SERIAL PRIMARY KEY,
        directive_id INTEGER REFERENCES genesis_bridge.directives(id),
        action VARCHAR(50) NOT NULL,
        details JSONB DEFAULT '{}'::jsonb,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    
    CREATE TABLE IF NOT EXISTS genesis_bridge.health_log (
        id SERIAL PRIMARY KEY,
        component VARCHAR(50) NOT NULL,
        status VARCHAR(20) NOT NULL,
        details JSONB DEFAULT '{}'::jsonb,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    
    CREATE INDEX IF NOT EXISTS idx_directives_status ON genesis_bridge.directives(status);
    CREATE INDEX IF NOT EXISTS idx_directives_priority ON genesis_bridge.directives(priority DESC);
    CREATE INDEX IF NOT EXISTS idx_directives_created ON genesis_bridge.directives(created_at DESC);
    CREATE INDEX IF NOT EXISTS idx_directives_uuid ON genesis_bridge.directives(uuid);
    """
    
    with db_pool.get_connection() as conn:
        with conn.cursor() as cur:
            cur.execute(schema_sql)
        conn.commit()
    logger.info("Database schema initialized")

# ============================================================================
# API MODELS
# ============================================================================

class DirectiveCreate(BaseModel):
    command_text: str
    priority: int = 5
    source: str = "dashboard"
    metadata: Dict[str, Any] = {}

class DirectiveResponse(BaseModel):
    uuid: str
    command_text: str
    priority: int
    status: str
    created_at: datetime
    updated_at: datetime
    completed_at: Optional[datetime]
    response_data: Optional[Dict]
    error_message: Optional[str]

class HealthResponse(BaseModel):
    status: str
    timestamp: str
    components: Dict[str, Any]

class StatusResponse(BaseModel):
    bridge_uuid: str
    status: str
    uptime_seconds: float
    directives_today: Dict[str, int]
    last_activity: Optional[str]

# ============================================================================
# AUTHENTICATION
# ============================================================================

async def verify_api_key(x_api_key: Optional[str] = Header(None)) -> str:
    """Verify API key from header."""
    if x_api_key is None:
        raise HTTPException(status_code=401, detail="Missing X-API-Key header")
    if x_api_key != API_KEY:
        raise HTTPException(status_code=403, detail="Invalid API key")
    return x_api_key

# ============================================================================
# API ENDPOINTS
# ============================================================================

@app.on_event("startup")
async def startup_event():
    """Initialize database and application on startup."""
    logger.info("Starting AIVA Voice Command Bridge...")
    db_pool.initialize()
    setup_schema()
    logger.info("Application started successfully")

@app.get("/bridge/status", response_model=StatusResponse)
async def get_bridge_status(api_key: str = Depends(verify_api_key)):
    """Get current bridge status."""
    bridge_state.increment_requests()
    
    with db_pool.get_connection() as conn:
        with conn.cursor(cursor_factory=RealDictCursor) as cur:
            # Optimized query with proper indexing
            cur.execute("""
                SELECT 
                    COUNT(*) FILTER (WHERE status = 'pending') as pending,
                    COUNT(*) FILTER (WHERE status = 'processing') as processing,
                    COUNT(*) FILTER (WHERE status = 'completed') as completed,
                    COUNT(*) FILTER (WHERE status = 'failed') as failed,
                    MAX(created_at) as last_activity
                FROM genesis_bridge.directives
                WHERE created_at >= CURRENT_DATE
            """)
            row = cur.fetchone()
            
            last_activity = row['last_activity'].isoformat() if row['last_activity'] else None
            
            return StatusResponse(
                bridge_uuid="genesis-bridge-001",
                status="operational",
                uptime_seconds=bridge_state.get_uptime(),
                directives_today={
                    "pending": int(row['pending']),
                    "processing": int(row['processing']),
                    "completed": int(row['completed']),
                    "failed": int(row['failed'])
                },
                last_activity=last_activity
            )

@app.get("/bridge/directives", response_model=List[DirectiveResponse])
async def get_directives(
    api_key: str = Depends(verify_api_key),
    status: Optional[str] = None,
    limit: int = 50
):
    """Get active directives."""
    bridge_state.increment_requests()
    
    with db_pool.get_connection() as conn:
        with conn.cursor(cursor_factory=RealDictCursor) as cur:
            if status:
                cur.execute("""
                    SELECT uuid, command_text, priority, status, created_at, 
                           updated_at, completed_at, response_data, error_message
                    FROM genesis_bridge.directives
                    WHERE status = %s
                    ORDER BY priority DESC, created_at DESC
                    LIMIT %s
                """, (status, limit))
            else:
                cur.execute("""
                    SELECT uuid, command_text, priority, status, created_at,
                           updated_at, completed_at, response_data, error_message
                    FROM genesis_bridge.directives
                    WHERE status IN ('pending', 'processing')
                    ORDER BY priority DESC, created_at ASC
                    LIMIT %s
                """, (limit,))
            
            rows = cur.fetchall()
            return [
                DirectiveResponse(
                    uuid=str(row['uuid']),
                    command_text=row['command_text'],
                    priority=row['priority'],
                    status=row['status'],
                    created_at=row['created_at'],
                    updated_at=row['updated_at'],
                    completed_at=row['completed_at'],
                    response_data=row['response_data'],
                    error_message=row['error_message']
                )
                for row in rows
            ]

@app.get("/bridge/history", response_model=List[DirectiveResponse])
async def get_history(
    api_key: str = Depends(verify_api_key),
    limit: int = 10
):
    """Get recent completed/failed directives."""
    bridge_state.increment_requests()
    
    with db_pool.get_connection() as conn:
        with conn.cursor(cursor_factory=RealDictCursor) as cur:
            # Optimized: get response time from completed_at - created_at
            cur.execute("""
                SELECT uuid, command_text, priority, status, created_at,
                       updated_at, completed_at, response_data, error_message,
                       EXTRACT(EPOCH FROM (completed_at - created_at)) as response_time_seconds
                FROM genesis_bridge.directives
                WHERE status IN ('completed', 'failed')
                ORDER BY completed_at DESC
                LIMIT %s
            """, (limit,))
            
            rows = cur.fetchall()
            return [
                DirectiveResponse(
                    uuid=str(row['uuid']),
                    command_text=row['command_text'],
                    priority=row['priority'],
                    status=row['status'],
                    created_at=row['created_at'],
                    updated_at=row['updated_at'],
                    completed_at=row['completed_at'],
                    response_data={** (row['response_data'] or {}), "response_time_seconds": row.get('response_time_seconds')},
                    error_message=row['error_message']
                )
                for row in rows
            ]

@app.post("/bridge/directives", response_model=DirectiveResponse)
async def create_directive(
    directive: DirectiveCreate,
    api_key: str = Depends(verify_api_key)
):
    """Create a new directive."""
    bridge_state.increment_requests()
    
    with db_pool.get_connection() as conn:
        with conn.cursor(cursor_factory=RealDictCursor) as cur:
            cur.execute("""
                INSERT INTO genesis_bridge.directives 
                (command_text, priority, source, metadata, status)
                VALUES (%s, %s, %s, %s, 'pending')
                RETURNING uuid, command_text, priority, status, created_at, 
                         updated_at, completed_at, response_data, error_message
            """, (directive.command_text, directive.priority, directive.source, 
                  str(directive.metadata)))
            row = cur.fetchone()
            conn.commit()
            
            return DirectiveResponse(
                uuid=str(row['uuid']),
                command_text=row['command_text'],
                priority=row['priority'],
                status=row['status'],
                created_at=row['created_at'],
                updated_at=row['updated_at'],
                completed_at=row['completed_at'],
                response_data=row['response_data'],
                error_message=row['error_message']
            )

@app.post("/bridge/actions/clear-completed")
async def clear_completed(api_key: str = Depends(verify_api_key)):
    """Clear completed directives older than 24 hours."""
    bridge_state.increment_requests()
    
    with db_pool.get_connection() as conn:
        with conn.cursor() as cur:
            cur.execute("""
                DELETE FROM genesis_bridge.directives
                WHERE status IN ('completed', 'failed')
                AND completed_at < CURRENT_TIMESTAMP - INTERVAL '24 hours'
            """)
            deleted = cur.rowcount
            conn.commit()
            
    return {"deleted": deleted, "timestamp": datetime.utcnow().isoformat()}

@app.post("/bridge/actions/force-poll")
async def force_poll(api_key: str = Depends(verify_api_key)):
    """Trigger a manual poll."""
    bridge_state.increment_requests()
    return {"status": "poll_triggered", "timestamp": datetime.utcnow().isoformat()}

@app.get("/bridge/health", response_model=HealthResponse)
async def get_health(api_key: str = Depends(verify_api_key)):
    """Get system health status."""
    bridge_state.increment_requests()
    
    db_health = db_pool.health_check()
    state_stats = bridge_state.get_stats()
    
    return HealthResponse(
        status="healthy" if db_health["status"] == "healthy" else "degraded",
        timestamp=datetime.utcnow().isoformat(),
        components={
            "api": {
                "status": "healthy",
                "uptime_seconds": state_stats["uptime_seconds"],
                "request_count": state_stats["request_count"]
            },
            "database": db_health,
            "poller": {
                "status": "active" if state_stats["poller_active"] else "inactive",
                "active": state_stats["poller_active"]
            },
            "websocket": {
                "status": "operational",
                "clients": state_stats["ws_clients"]
            }
        }
    )

# ============================================================================
# DASHBOARD HTML (Mobile-optimized, Dark theme)
# ============================================================================

DASHBOARD_HTML = r'''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="theme-color" content="#0B0C0E">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <title>AIVA Command Bridge</title>
    <link rel="manifest" href="/bridge/manifest.json">
    <style>
        :root {
            --bg-primary: #0B0C0E;
            --bg-secondary: #151920;
            --bg-card: #1a1f2a;
            --accent: #3B82F6;
            --accent-hover: #2563EB;
            --text-primary: #F1F5F9;
            --text-secondary: #94A3B8;
            --text-muted: #64748B;
            --success: #22C55E;
            --warning: #F59E0B;
            --danger: #EF4444;
            --priority-10: #DC2626;
            --priority-8: #EA580C;
            --priority-6: #CA8A04;
            --priority-4: #65A30D;
            --priority-2: #475569;
            --border: #2D3748;
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: var(--bg-primary);
            color: var(--text-primary);
            min-height: 100vh;
            line-height: 1.5;
            padding-bottom: env(safe-area-inset-bottom);
        }
        
        .header {
            background: var(--bg-secondary);
            padding: 16px;
            position: sticky;
            top: 0;
            z-index: 100;
            border-bottom: 1px solid var(--border);
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        
        .header h1 {
            font-size: 18px;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .status-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            display: inline-block;
        }
        
        .status-dot.healthy { background: var(--success); box-shadow: 0 0 8px var(--success); }
        .status-dot.degraded { background: var(--warning); box-shadow: 0 0 8px var(--warning); }
        .status-dot.unhealthy { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
        
        .container {
            padding: 16px;
            max-width: 600px;
            margin: 0 auto;
        }
        
        .section {
            margin-bottom: 20px;
        }
        
        .section-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;
        }
        
        .section-title {
            font-size: 14px;
            font-weight: 600;
            color: var(--text-secondary);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        
        .metrics-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
        }
        
        .metric-card {
            background: var(--bg-card);
            border-radius: 12px;
            padding: 16px;
            border: 1px solid var(--border);
        }
        
        .metric-value {
            font-size: 28px;
            font-weight: 700;
            color: var(--accent);
        }
        
        .metric-label {
            font-size: 12px;
            color: var(--text-muted);
            margin-top: 4px;
        }
        
        .directive-card {
            background: var(--bg-card);
            border-radius: 12px;
            padding: 16px;
            margin-bottom: 12px;
            border: 2px solid var(--border);
            transition: all 0.3s ease;
            animation: fadeIn 0.3s ease;
        }
        
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }
        
        .directive-card.priority-10 {
            border-color: var(--priority-10);
            animation: pulse 1.5s infinite;
        }
        
        .directive-card.priority-8, .directive-card.priority-9 {
            border-color: var(--priority-8);
        }
        
        .directive-card.priority-6, .directive-card.priority-7 {
            border-color: var(--priority-6);
        }
        
        .directive-card.priority-4, .directive-card.priority-5 {
            border-color: var(--border);
        }
        
        .directive-card.priority-1, .directive-card.priority-2, .directive-card.priority-3 {
            border-color: var(--priority-2);
            opacity: 0.7;
        }
        
        .directive-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 8px;
        }
        
        .directive-command {
            font-size: 16px;
            font-weight: 500;
            flex: 1;
        }
        
        .directive-priority {
            font-size: 11px;
            font-weight: 600;
            padding: 4px 8px;
            border-radius: 4px;
            background: var(--bg-secondary);
        }
        
        .directive-meta {
            display: flex;
            gap: 12px;
            font-size: 12px;
            color: var(--text-muted);
        }
        
        .directive-status {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 11px;
            font-weight: 500;
            text-transform: uppercase;
        }
        
        .directive-status.pending { background: rgba(245, 158, 11, 0.2); color: var(--warning); }
        .directive-status.processing { background: rgba(59, 130, 246, 0.2); color: var(--accent); }
        .directive-status.completed { background: rgba(34, 197, 94, 0.2); color: var(--success); }
        .directive-status.failed { background: rgba(239, 68, 68, 0.2); color: var(--danger); }
        
        .health-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
        }
        
        .health-item {
            background: var(--bg-card);
            border-radius: 8px;
            padding: 12px;
            display: flex;
            flex-direction: column;
            gap: 4px;
        }
        
        .health-label {
            font-size: 11px;
            color: var(--text-muted);
            text-transform: uppercase;
        }
        
        .health-value {
            font-size: 14px;
            font-weight: 500;
            display: flex;
            align-items: center;
            gap: 6px;
        }
        
        .health-value.healthy { color: var(--success); }
        .health-value.unhealthy { color: var(--danger); }
        
        .actions-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 8px;
        }
        
        .action-btn {
            background: var(--bg-card);
            border: 1px solid var(--border);
            color: var(--text-primary);
            padding: 12px 8px;
            border-radius: 8px;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 4px;
        }
        
        .action-btn:active {
            background: var(--bg-secondary);
            transform: scale(0.98);
        }
        
        .action-btn svg {
            width: 20px;
            height: 20px;
            stroke: var(--accent);
        }
        
        .empty-state {
            text-align: center;
            padding: 32px;
            color: var(--text-muted);
        }
        
        .error-banner {
            background: rgba(239, 68, 68, 0.1);
            border: 1px solid var(--danger);
            color: var(--danger);
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 16px;
            display: none;
        }
        
        .error-banner.show {
            display: block;
        }
        
        .last-updated {
            font-size: 11px;
            color: var(--text-muted);
            text-align: center;
            padding: 8px;
        }
        
        .loading {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid var(--border);
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }
        
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        
        @media (min-width: 768px) {
            .container { max-width: 800px; }
            .metrics-grid { grid-template-columns: repeat(4, 1fr); }
            .directive-card { padding: 20px; }
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>
            <span class="status-dot" id="connectionStatus"></span>
            AIVA Command Bridge
        </h1>
        <span id="lastUpdated" class="last-updated"></span>
    </div>
    
    <div class="container">
        <div class="error-banner" id="errorBanner">
            Unable to connect to API. Please check your connection.
        </div>
        
        <!-- Queue Metrics -->
        <div class="section">
            <div class="section-header">
                <span class="section-title">Queue Metrics</span>
            </div>
            <div class="metrics-grid">
                <div class="metric-card">
                    <div class="metric-value" id="metricPending">-</div>
                    <div class="metric-label">Pending</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="metricProcessing">-</div>
                    <div class="metric-label">Processing</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="metricCompleted">-</div>
                    <div class="metric-label">Completed Today</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="metricFailed">-</div>
                    <div class="metric-label">Failed Today</div>
                </div>
            </div>
        </div>
        
        <!-- Active Directives -->
        <div class="section">
            <div class="section-header">
                <span class="section-title">Active Directives</span>
                <span id="activeCount" style="font-size: 12px; color: var(--text-muted)"></span>
            </div>
            <div id="directivesList">
                <div class="empty-state">Loading directives...</div>
            </div>
        </div>
        
        <!-- Recent Completions -->
        <div class="section">
            <div class="section-header">
                <span class="section-title">Recent Completions</span>
            </div>
            <div id="historyList">
                <div class="empty-state">Loading history...</div>
            </div>
        </div>
        
        <!-- System Health -->
        <div class="section">
            <div class="section-header">
                <span class="section-title">System Health</span>
            </div>
            <div class="health-grid" id="healthGrid">
                <div class="health-item">
                    <span class="health-label">API Uptime</span>
                    <span class="health-value" id="healthUptime">-</span>
                </div>
                <div class="health-item">
                    <span class="health-label">Database</span>
                    <span class="health-value" id="healthDb">-</span>
                </div>
                <div class="health-item">
                    <span class="health-label">Poller</span>
                    <span class="health-value" id="healthPoller">-</span>
                </div>
                <div class="health-item">
                    <span class="health-label">WebSocket</span>
                    <span class="health-value" id="healthWs">-</span>
                </div>
            </div>
        </div>
        
        <!-- Quick Actions -->
        <div class="section">
            <div class="section-header">
                <span class="section-title">Quick Actions</span>
            </div>
            <div class="actions-grid">
                <button class="action-btn" onclick="sendTestDirective()">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>
                    </svg>
                    Test Directive
                </button>
                <button class="action-btn" onclick="clearCompleted()">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
                    </svg>
                    Clear Completed
                </button>
                <button class="action-btn" onclick="forcePoll()">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="23 4 23 10 17 10M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
                    </svg>
                    Force Poll
                </button>
            </div>
        </div>
        
        <div class="last-updated">
            Auto-refresh: <span id="refreshTimer">10</span>s | <a href="#" onclick="toggleSound()" id="soundToggle">Sound: Off</a>
        </div>
    </div>
    
    <script>
        // Configuration
        const REFRESH_INTERVAL = 10000;
        let apiKey = localStorage.getItem('aiva_api_key') || new URLSearchParams(window.location.search).get('api_key') || '';
        let soundEnabled = false;
        let refreshTimer = 10;
        let timerInterval = null;
        
        // Get API key from URL or prompt
        if (!apiKey) {
            apiKey = prompt('Enter API Key:', '') || '';
            if (apiKey) localStorage.setItem('aiva_api_key', apiKey);
        }
        
        const headers = { 'X-API-Key': apiKey };
        
        // Utility functions
        function formatUptime(seconds) {
            const hrs = Math.floor(seconds / 3600);
            const mins = Math.floor((seconds % 3600) / 60);
            if (hrs > 0) return `${hrs}h ${mins}m`;
            return `${mins}m`;
        }
        
        function formatAge(dateStr) {
            const date = new Date(dateStr);
            const now = new Date();
            const diff = Math.floor((now - date) / 1000);
            if (diff < 60) return `${diff}s ago`;
            if (diff < 3600) return `${Math.floor(diff/60)}m ago`;
            return `${Math.floor(diff/3600)}h ago`;
        }
        
        function getPriorityClass(priority) {
            return `priority-${Math.max(1, Math.min(10, priority))}`;
        }
        
        function showError(show) {
            document.getElementById('errorBanner').classList.toggle('show', show);
        }
        
        function toggleSound() {
            soundEnabled = !soundEnabled;
            document.getElementById('soundToggle').textContent = `Sound: ${soundEnabled ? 'On' : 'Off'}`;
            if (soundEnabled) playNotificationSound();
        }
        
        function playNotificationSound() {
            try {
                const ctx = new (window.AudioContext || window.webkitAudioContext)();
                const osc = ctx.createOscillator();
                const gain = ctx.createGain();
                osc.connect(gain);
                gain.connect(ctx.destination);
                osc.frequency.value = 800;
                gain.gain.value = 0.1;
                osc.start();
                gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3);
                osc.stop(ctx.currentTime + 0.3);
            } catch(e) {}
        }
        
        // API calls
        async function fetchWithAuth(url) {
            const response = await fetch(url, { headers });
            if (response.status === 401 || response.status === 403) {
                localStorage.removeItem('aiva_api_key');
                window.location.reload();
            }
            if (!response.ok) throw new Error(`HTTP ${response.status}`);
            return response.json();
        }
        
        async function loadStatus() {
            try {
                const data = await fetchWithAuth('/bridge/status');
                document.getElementById('metricPending').textContent = data.directives_today.pending;
                document.getElementById('metricProcessing').textContent = data.directives_today.processing;
                document.getElementById('metricCompleted').textContent = data.directives_today.completed;
                document.getElementById('metricFailed').textContent = data.directives_today.failed;
                showError(false);
            } catch(e) {
                showError(true);
            }
        }
        
        async function loadDirectives() {
            try {
                const data = await fetchWithAuth('/bridge/directives?limit=20');
                const container = document.getElementById('directivesList');
                document.getElementById('activeCount').textContent = `${data.length} active`;
                
                if (data.length === 0) {
                    container.innerHTML = '<div class="empty-state">No active directives</div>';
                    return;
                }
                
                container.innerHTML = data.map(d => `
                    <div class="directive-card ${getPriorityClass(d.priority)}">
                        <div class="directive-header">
                            <span class="directive-command">${d.command_text}</span>
                            <span class="directive-priority">P${d.priority}</span>
                        </div>
                        <div class="directive-meta">
                            <span class="directive-status ${d.status}">${d.status}</span>
                            <span>${formatAge(d.created_at)}</span>
                        </div>
                    </div>
                `).join('');
                
                // Play sound for high priority
                if (soundEnabled && data.some(d => d.priority >= 9)) {
                    playNotificationSound();
                }
            } catch(e) {
                document.getElementById('directivesList').innerHTML = '<div class="empty-state">Failed to load</div>';
            }
        }
        
        async function loadHistory() {
            try {
                const data = await fetchWithAuth('/bridge/history?limit=10');
                const container = document.getElementById('historyList');
                
                if (data.length === 0) {
                    container.innerHTML = '<div class="empty-state">No recent completions</div>';
                    return;
                }
                
                container.innerHTML = data.map(d => `
                    <div class="directive-card ${getPriorityClass(d.priority)}">
                        <div class="directive-header">
                            <span class="directive-command">${d.command_text}</span>
                            <span class="directive-priority">P${d.priority}</span>
                        </div>
                        <div class="directive-meta">
                            <span class="directive-status ${d.status}">${d.status}</span>
                            ${d.response_data?.response_time_seconds ? `<span>${Math.round(d.response_data.response_time_seconds)}s</span>` : ''}
                            <span>${formatAge(d.completed_at)}</span>
                        </div>
                    </div>
                `).join('');
            } catch(e) {
                document.getElementById('historyList').innerHTML = '<div class="empty-state">Failed to load</div>';
            }
        }
        
        async function loadHealth() {
            try {
                const data = await fetchWithAuth('/bridge/health');
                
                // Update connection status
                const statusEl = document.getElementById('connectionStatus');
                statusEl.className = 'status-dot ' + (data.status === 'healthy' ? 'healthy' : 'degraded');
                
                // Health components
                const components = data.components;
                
                document.getElementById('healthUptime').textContent = formatUptime(components.api.uptime_seconds);
                document.getElementById('healthUptime').className = 'health-value healthy';
                
                const dbEl = document.getElementById('healthDb');
                dbEl.textContent = components.database.status;
                dbEl.className = 'health-value ' + (components.database.status === 'healthy' ? 'healthy' : 'unhealthy');
                
                const pollerEl = document.getElementById('healthPoller');
                pollerEl.textContent = components.poller.active ? 'Active' : 'Inactive';
                pollerEl.className = 'health-value ' + (components.poller.active ? 'healthy' : 'unhealthy');
                
                const wsEl = document.getElementById('healthWs');
                wsEl.textContent = components.websocket.clients + ' clients';
                wsEl.className = 'health-value healthy';
                
            } catch(e) {
                document.getElementById('connectionStatus').className = 'status-dot unhealthy';
            }
        }
        
        // Actions
        async function sendTestDirective() {
            try {
                await fetchWithAuth('/bridge/directives', {
                    method: 'POST',
                    headers: { ...headers, 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        command_text: 'Test directive from dashboard',
                        priority: Math.floor(Math.random() * 5) + 5,
                        source: 'dashboard'
                    })
                });
                refreshAll();
            } catch(e) {
                alert('Failed to send test directive');
            }
        }
        
        async function clearCompleted() {
            if (!confirm('Clear completed directives older than 24 hours?')) return;
            try {
                await fetchWithAuth('/bridge/actions/clear-completed', { method: 'POST' });
                refreshAll();
            } catch(e) {
                alert('Failed to clear completed');
            }
        }
        
        async function forcePoll() {
            try {
                await fetchWithAuth('/bridge/actions/force-poll', { method: 'POST' });
                setTimeout(refreshAll, 1000);
            } catch(e) {
                alert('Failed to trigger poll');
            }
        }
        
        // Refresh cycle
        async function refreshAll() {
            await Promise.all([
                loadStatus(),
                loadDirectives(),
                loadHistory(),
                loadHealth()
            ]);
            document.getElementById('lastUpdated').textContent = new Date().toLocaleTimeString();
            resetTimer();
        }
        
        function resetTimer() {
            refreshTimer = 10;
            document.getElementById('refreshTimer').textContent = refreshTimer;
        }
        
        function startTimer() {
            if (timerInterval) clearInterval(timerInterval);
            timerInterval = setInterval(() => {
                refreshTimer--;
                document.getElementById('refreshTimer').textContent = refreshTimer;
                if (refreshTimer <= 0) {
                    refreshAll();
                }
            }, 1000);
        }
        
        // Initialize
        document.addEventListener('DOMContentLoaded', () => {
            refreshAll();
            startTimer();
        });
        
        // Handle visibility change to pause when backgrounded
        document.addEventListener('visibilitychange', () => {
            if (document.hidden) {
                clearInterval(timerInterval);
            } else {
                refreshAll();
                startTimer();
            }
        });
    </script>
</body>
</html>
'''

MANIFEST_JSON = '''{
    "name": "AIVA Command Bridge",
    "short_name": "AIVA Bridge",
    "description": "Genesis Voice Command Bridge Dashboard",
    "start_url": "/bridge/dashboard",
    "display": "standalone",
    "background_color": "#0B0C0E",
    "theme_color": "#0B0C0E",
    "icons": [
        {
            "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect fill='%230B0C0E' width='100' height='100' rx='20'/><circle fill='%233B82F6' cx='50' cy='50' r='30'/></svg>",
            "sizes": "192x192",
            "type": "image/svg+xml"
        }
    ]
}
'''

# ============================================================================
# DASHBOARD ENDPOINTS
# ============================================================================

@app.get("/bridge/dashboard", response_class=HTMLResponse)
async def dashboard():
    """Serve the mobile dashboard."""
    return HTMLResponse(content=DASHBOARD_HTML, media_type="text/html")

@app.get("/bridge/manifest.json")
async def manifest():
    """Serve PWA manifest."""
    return JSONResponse(content=json.loads(MANIFEST_JSON))

# ============================================================================
# HEALTH CHECK (No auth required)
# ============================================================================

@app.get("/health")
async def health_check():
    """Public health check endpoint."""
    db_health = db_pool.health_check()
    return {
        "status": "healthy" if db_health["status"] == "healthy" else "degraded",
        "timestamp": datetime.utcnow().isoformat(),
        "database": db_health["status"]
    }

# ============================================================================
# MAIN
# ============================================================================

if __name__ == "__main__":
    import json
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8000,
        log_level="info"
    )