"""Topology API routes -- network topology and latency data for the dashboard.

Mounted under ``/api`` by :func:`llmhost.proxy.app.create_app`.
Exposes the current network topology snapshot and manual ping triggers.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from fastapi import APIRouter
from starlette.requests import Request  # noqa: TC002

if TYPE_CHECKING:
    from llmhosts.topology.mapper import TopologyMapper

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api", tags=["topology"])


def _get_mapper(request: Request) -> TopologyMapper | None:
    """Retrieve the TopologyMapper from ``app.state``, or None."""
    return getattr(request.app.state, "topology_mapper", None)


# ---------------------------------------------------------------------------
# GET /api/topology -- current network topology
# ---------------------------------------------------------------------------


@router.get("/topology")
async def get_topology(request: Request) -> dict[str, Any]:
    """Get the current network topology with all backend nodes and latencies."""
    mapper = _get_mapper(request)
    if mapper is None:
        return {
            "timestamp": None,
            "nodes": [],
            "total_backends": 0,
            "healthy_backends": 0,
            "avg_latency_ms": 0.0,
        }

    snapshot = mapper.get_snapshot()
    return {
        "timestamp": snapshot.timestamp.isoformat(),
        "nodes": [
            {
                "backend_id": node.backend_id,
                "backend_type": node.backend_type,
                "name": node.name,
                "url": node.url,
                "is_local": node.is_local,
                "status": node.status.value,
                "latency_ms": node.latency_ms,
                "latency_bucket": node.latency_bucket.value,
                "p50_ms": node.p50_ms,
                "p95_ms": node.p95_ms,
                "p99_ms": node.p99_ms,
                "models": node.models,
                "last_seen": node.last_seen.isoformat() if node.last_seen else None,
                "last_check": node.last_check.isoformat() if node.last_check else None,
                "consecutive_failures": node.consecutive_failures,
                "uptime_percent": node.uptime_percent,
                "trend": node.trend,
            }
            for node in snapshot.nodes
        ],
        "total_backends": snapshot.total_backends,
        "healthy_backends": snapshot.healthy_backends,
        "avg_latency_ms": snapshot.avg_latency_ms,
    }


# ---------------------------------------------------------------------------
# POST /api/topology/ping -- trigger manual health check
# ---------------------------------------------------------------------------


@router.post("/topology/ping")
async def ping_topology(request: Request) -> dict[str, Any]:
    """Trigger a manual health check on all backends and return updated topology."""
    mapper = _get_mapper(request)
    if mapper is None:
        return {
            "timestamp": None,
            "nodes": [],
            "total_backends": 0,
            "healthy_backends": 0,
            "avg_latency_ms": 0.0,
        }

    snapshot = await mapper.ping_all()
    return {
        "timestamp": snapshot.timestamp.isoformat(),
        "nodes": [
            {
                "backend_id": node.backend_id,
                "backend_type": node.backend_type,
                "name": node.name,
                "url": node.url,
                "is_local": node.is_local,
                "status": node.status.value,
                "latency_ms": node.latency_ms,
                "latency_bucket": node.latency_bucket.value,
                "p50_ms": node.p50_ms,
                "p95_ms": node.p95_ms,
                "p99_ms": node.p99_ms,
                "models": node.models,
                "last_seen": node.last_seen.isoformat() if node.last_seen else None,
                "last_check": node.last_check.isoformat() if node.last_check else None,
                "consecutive_failures": node.consecutive_failures,
                "uptime_percent": node.uptime_percent,
                "trend": node.trend,
            }
            for node in snapshot.nodes
        ],
        "total_backends": snapshot.total_backends,
        "healthy_backends": snapshot.healthy_backends,
        "avg_latency_ms": snapshot.avg_latency_ms,
    }
