"""
Dashboard Endpoints
===================
War Room (戰情室) data aggregation with SSE streaming

Endpoints:
- GET  /dashboard         - Aggregated dashboard data
- GET  /dashboard/stream  - SSE real-time updates
- GET  /dashboard/hosts   - Four-host status overview
"""

import asyncio
from datetime import UTC, datetime
from typing import Any

from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

from src.core.config import settings
from src.core.logging import get_logger
from src.core.sse import EventPublisher, EventType, SSEEvent, get_publisher
from src.services.host_aggregator import AggregatedStatus, HostAggregator

router = APIRouter()
logger = get_logger("awoooi.dashboard")


# =============================================================================
# Response Models
# =============================================================================

class BaselineResponse(BaseModel):
    """Dynamic baseline data"""
    baseline_value: float
    std_deviation: float
    sigma_deviation: float | None = None
    window_hours: int = 24


class HostMetricsResponse(BaseModel):
    """Host metrics with baseline"""
    cpu_percent: float | None = None
    memory_percent: float | None = None
    disk_percent: float | None = None
    load_avg_1m: float | None = None
    uptime_hours: float | None = None
    cpu_baseline: BaselineResponse | None = None
    memory_baseline: BaselineResponse | None = None


class HostStatusResponse(BaseModel):
    """Host status for API response"""
    ip: str
    name: str
    role: str
    status: str
    services: list[dict[str, Any]]
    metrics: HostMetricsResponse | None = None
    last_check: datetime | None = None


class DashboardResponse(BaseModel):
    """Dashboard aggregated data"""
    timestamp: datetime
    environment: str
    mock_mode: bool
    overall_status: str
    hosts: list[HostStatusResponse]
    alerts_count: int
    pending_approvals: int


# =============================================================================
# Helper Functions
# =============================================================================

def aggregated_to_response(agg: AggregatedStatus) -> DashboardResponse:
    """Convert AggregatedStatus to API response"""
    hosts = []
    for h in agg.hosts:
        hosts.append(HostStatusResponse(
            ip=h.ip,
            name=h.name,
            role=h.role.value,
            status=h.status,
            services=[
                {
                    "name": s.name,
                    "status": s.status,
                    "port": s.port,
                    "latency_ms": s.latency_ms,
                    "error": s.error,
                }
                for s in h.services
            ],
            metrics=HostMetricsResponse(
                cpu_percent=h.metrics.cpu_percent,
                memory_percent=h.metrics.memory_percent,
                disk_percent=h.metrics.disk_percent,
                load_avg_1m=h.metrics.load_avg_1m,
                uptime_hours=h.metrics.uptime_hours,
                cpu_baseline=BaselineResponse(
                    baseline_value=h.metrics.cpu_baseline.baseline_value,
                    std_deviation=h.metrics.cpu_baseline.std_deviation,
                    sigma_deviation=h.metrics.cpu_baseline.sigma_deviation,
                    window_hours=h.metrics.cpu_baseline.window_hours,
                ) if h.metrics.cpu_baseline else None,
                memory_baseline=BaselineResponse(
                    baseline_value=h.metrics.memory_baseline.baseline_value,
                    std_deviation=h.metrics.memory_baseline.std_deviation,
                    sigma_deviation=h.metrics.memory_baseline.sigma_deviation,
                    window_hours=h.metrics.memory_baseline.window_hours,
                ) if h.metrics.memory_baseline else None,
            ) if h.metrics else None,
            last_check=h.last_check,
        ))

    return DashboardResponse(
        timestamp=agg.timestamp,
        environment=agg.environment,
        mock_mode=agg.mock_mode,
        overall_status=agg.overall_status,
        hosts=hosts,
        alerts_count=agg.alerts_count,
        pending_approvals=agg.pending_approvals,
    )


# =============================================================================
# SSE Background Publisher
# =============================================================================

async def dashboard_update_loop(publisher: EventPublisher) -> None:
    """
    Background task: Periodically fetch and publish dashboard updates

    Runs every CACHE_TTL_HOST_STATUS seconds (default 30s)
    """
    while publisher.is_running:
        try:
            # Fetch aggregated status
            status = await HostAggregator.fetch_all()

            # Publish to all connected clients
            event = SSEEvent(
                type=EventType.HOST_UPDATE,
                data={
                    "overall_status": status.overall_status,
                    "hosts": [
                        {
                            "ip": h.ip,
                            "name": h.name,
                            "status": h.status,
                            "metrics": {
                                "cpu_percent": h.metrics.cpu_percent,
                                "memory_percent": h.metrics.memory_percent,
                            } if h.metrics else None,
                        }
                        for h in status.hosts
                    ],
                },
            )

            sent_count = await publisher.publish(event, topic="dashboard")

            if sent_count > 0:
                logger.debug(
                    "dashboard_update_published",
                    sent_count=sent_count,
                    overall_status=status.overall_status,
                )

            await asyncio.sleep(settings.CACHE_TTL_HOST_STATUS)

        except asyncio.CancelledError:
            break
        except Exception as e:
            logger.error("dashboard_update_error", error=str(e))
            await asyncio.sleep(5)  # Retry after error


# Global update task reference
_update_task: asyncio.Task | None = None


async def ensure_update_loop(publisher: EventPublisher) -> None:
    """Ensure the update loop is running"""
    global _update_task
    if _update_task is None or _update_task.done():
        _update_task = asyncio.create_task(dashboard_update_loop(publisher))


# =============================================================================
# Endpoints
# =============================================================================

@router.get("/dashboard", response_model=DashboardResponse)
async def get_dashboard() -> DashboardResponse:
    """
    Get aggregated dashboard data

    Fetches status from all four hosts using asyncio.gather.
    Returns CPU/Memory metrics when MOCK_MODE is enabled.
    """
    logger.info("dashboard_fetch")

    status = await HostAggregator.fetch_all()
    return aggregated_to_response(status)


@router.get("/dashboard/stream")
async def stream_dashboard(request: Request) -> StreamingResponse:
    """
    SSE real-time dashboard updates

    Enterprise-grade SSE implementation with:
    - Automatic disconnect detection
    - Resource cleanup on disconnect
    - Heartbeat mechanism (every 15s)
    - Backpressure handling

    Client Usage (JavaScript):
    ```javascript
    const es = new EventSource('/api/v1/dashboard/stream');
    es.addEventListener('host_update', (e) => {
        const data = JSON.parse(e.data);
        console.log('Host update:', data);
    });
    es.addEventListener('heartbeat', (e) => {
        console.log('Heartbeat received');
    });
    es.onerror = (e) => {
        console.log('Connection lost, reconnecting...');
    };
    ```

    Disconnect Detection:
    - When browser closes or navigates away
    - When network connection is lost
    - When client calls es.close()

    The server automatically detects disconnection via:
    1. asyncio.CancelledError on generator exit
    2. Heartbeat timeout detection
    3. Queue full backpressure
    """
    logger.info("dashboard_stream_connect", client_ip=request.client.host if request.client else "unknown")

    # Get publisher and ensure update loop is running
    pub = await get_publisher()
    await ensure_update_loop(pub)

    # Subscribe client to dashboard topic
    client = await pub.subscribe(
        topics=["dashboard"],
        metadata={"ip": request.client.host if request.client else "unknown"},
    )

    async def event_generator():
        """
        SSE event generator with disconnect detection

        The try/finally ensures cleanup happens even when:
        - Client disconnects (CancelledError)
        - Network error occurs
        - Server shuts down
        """
        try:
            async for data in pub.stream(client):
                # Check if client is still connected
                if await request.is_disconnected():
                    logger.info("dashboard_stream_client_disconnected", client_id=client.id)
                    break
                yield data

        except asyncio.CancelledError:
            # Client disconnected (browser closed, etc.)
            logger.info("dashboard_stream_cancelled", client_id=client.id)
            raise

        finally:
            # Cleanup is handled by pub.stream() finally block
            logger.info("dashboard_stream_cleanup", client_id=client.id)

    return StreamingResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache, no-store, must-revalidate",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",  # Disable Nginx buffering
            "Access-Control-Allow-Origin": "*",  # SSE requires this
        },
    )


@router.get("/dashboard/hosts")
async def get_hosts() -> dict:
    """
    Get four-host architecture status

    Returns the configured four-host IPs and their roles.
    """
    return {
        "hosts": settings.four_hosts,
        "timestamp": datetime.now(UTC).isoformat(),
    }


@router.get("/dashboard/stream/clients")
async def get_stream_clients() -> dict:
    """
    Get current SSE client count (debug endpoint)
    """
    pub = await get_publisher()
    return {
        "client_count": pub.client_count,
        "is_running": pub.is_running,
        "timestamp": datetime.now(UTC).isoformat(),
    }


@router.get("/dashboard/snapshot")
async def get_dashboard_snapshot() -> dict:
    """
    Full dashboard snapshot for SSE hydration

    Client workflow:
    1. Connect to /dashboard/stream (SSE)
    2. Immediately fetch /dashboard/snapshot
    3. Apply snapshot as initial state
    4. Process SSE events for incremental updates

    This ensures no alerts are missed during connection setup.
    """
    logger.info("dashboard_snapshot_fetch")

    status = await HostAggregator.fetch_all()

    # Convert to serializable dict
    hosts_data = []
    for h in status.hosts:
        hosts_data.append({
            "ip": h.ip,
            "name": h.name,
            "role": h.role.value,
            "status": h.status,
            "services": [
                {
                    "name": s.name,
                    "status": s.status,
                    "port": s.port,
                    "latency_ms": s.latency_ms,
                    "error": s.error,
                }
                for s in h.services
            ],
            "metrics": {
                "cpu_percent": h.metrics.cpu_percent,
                "memory_percent": h.metrics.memory_percent,
                "disk_percent": h.metrics.disk_percent,
                "load_avg_1m": h.metrics.load_avg_1m,
                "uptime_hours": h.metrics.uptime_hours,
                "cpu_baseline": {
                    "baseline_value": h.metrics.cpu_baseline.baseline_value,
                    "std_deviation": h.metrics.cpu_baseline.std_deviation,
                    "sigma_deviation": h.metrics.cpu_baseline.sigma_deviation,
                    "window_hours": h.metrics.cpu_baseline.window_hours,
                } if h.metrics.cpu_baseline else None,
                "memory_baseline": {
                    "baseline_value": h.metrics.memory_baseline.baseline_value,
                    "std_deviation": h.metrics.memory_baseline.std_deviation,
                    "sigma_deviation": h.metrics.memory_baseline.sigma_deviation,
                    "window_hours": h.metrics.memory_baseline.window_hours,
                } if h.metrics.memory_baseline else None,
            } if h.metrics else None,
            "last_check": h.last_check.isoformat(),
        })

    return {
        "timestamp": status.timestamp.isoformat(),
        "environment": status.environment,
        "mock_mode": status.mock_mode,
        "overall_status": status.overall_status,
        "hosts": hosts_data,
        "alerts_count": status.alerts_count,
        "pending_approvals": status.pending_approvals,
    }
