"""
Admin / diagnostic pages for the device modules (SFM, SLMM).

These pages live under /admin/{module} and exist purely so an operator can
peek under the hood and confirm the module is reachable, what data it's
holding, and whether the proxy from terra-view is healthy.

Routes:
  GET /admin/sfm   — SFM diagnostic page
  GET /admin/slmm  — SLMM diagnostic page

API helpers (called by the HTML pages via fetch):
  GET /api/admin/sfm/overview   — aggregated SFM health + db stats in one call
  GET /api/admin/slmm/overview  — aggregated SLMM health + device count

The pages are intentionally read-only.  Any actual administration of SFM
or SLMM happens in those modules directly.
"""

import logging
import os
from datetime import datetime, timezone
from typing import Any, Dict

import httpx
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse, JSONResponse
from sqlalchemy.orm import Session

from backend.database import get_db
from backend.templates_config import templates

log = logging.getLogger(__name__)

router = APIRouter()

SFM_BASE_URL  = os.getenv("SFM_BASE_URL",  "http://localhost:8200")
SLMM_BASE_URL = os.getenv("SLMM_BASE_URL", "http://localhost:8100")


# ── SFM ───────────────────────────────────────────────────────────────────────


@router.get("/admin/sfm", response_class=HTMLResponse)
def admin_sfm_page(request: Request):
    return templates.TemplateResponse("admin_sfm.html", {
        "request": request,
        "sfm_base_url": SFM_BASE_URL,
    })


@router.get("/admin/events", response_class=HTMLResponse)
def admin_events_page(request: Request):
    """SFM Event DB Manager — browse, flag, and delete events across all units."""
    return templates.TemplateResponse("admin_events.html", {
        "request": request,
        "sfm_base_url": SFM_BASE_URL,
    })


@router.get("/api/admin/sfm/overview")
async def admin_sfm_overview() -> JSONResponse:
    """Aggregated SFM diagnostic snapshot.

    Returns health, db stats, stale-table counts, per-unit summary, and
    recent events with forwarding latency.  Tolerant of partial failures:
    any individual sub-fetch error is captured into its section, so a flaky
    sub-endpoint doesn't break the whole page.
    """
    overview: Dict[str, Any] = {
        "sfm_base_url": SFM_BASE_URL,
        "checked_at":   datetime.now(timezone.utc).isoformat(),
        "health":       None,
        "reachable":    False,
        "units":        [],
        "events":       [],
        "stale": {
            "monitor_log": None,
            "sessions":    None,
        },
        "cache_stats":  None,
        "errors":       {},
    }

    async with httpx.AsyncClient(timeout=5.0) as client:
        # Health
        try:
            r = await client.get(f"{SFM_BASE_URL}/health")
            r.raise_for_status()
            overview["health"]    = r.json()
            overview["reachable"] = overview["health"].get("status") == "ok"
        except Exception as e:  # noqa: BLE001
            overview["errors"]["health"] = str(e)
            overview["reachable"] = False

        # If SFM is down, no point hitting the rest.
        if not overview["reachable"]:
            return JSONResponse(overview)

        # Units
        try:
            r = await client.get(f"{SFM_BASE_URL}/db/units")
            r.raise_for_status()
            overview["units"] = r.json() or []
        except Exception as e:  # noqa: BLE001
            overview["errors"]["units"] = str(e)

        # Recent events (newest 25 — bigger sample of the call-home stream)
        try:
            r = await client.get(f"{SFM_BASE_URL}/db/events", params={"limit": 25})
            r.raise_for_status()
            payload = r.json() or {}
            events = payload.get("events", []) or []
            # Compute forwarding latency: created_at (SFM ingest) − timestamp (event).
            now = datetime.now(timezone.utc)
            for ev in events:
                ev.pop("waveform_blob", None)
                ev.pop("a5_pickle_filename", None)
                ts_str = ev.get("timestamp")
                ca_str = ev.get("created_at")
                latency_seconds = None
                try:
                    if ts_str and ca_str:
                        ts = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
                        ca = datetime.fromisoformat(ca_str.replace("Z", "+00:00"))
                        if ts.tzinfo is None: ts = ts.replace(tzinfo=timezone.utc)
                        if ca.tzinfo is None: ca = ca.replace(tzinfo=timezone.utc)
                        latency_seconds = (ca - ts).total_seconds()
                except ValueError:
                    pass
                ev["forwarding_latency_seconds"] = latency_seconds
            overview["events"] = events
        except Exception as e:  # noqa: BLE001
            overview["errors"]["events"] = str(e)

        # Stale tables (deprecated by the watcher-forward pipeline but still
        # present in SFM's SQLite).  Surface as counts only.
        for key, path in (("monitor_log", "/db/monitor_log"),
                          ("sessions",    "/db/sessions")):
            try:
                r = await client.get(f"{SFM_BASE_URL}{path}", params={"limit": 1})
                r.raise_for_status()
                payload = r.json() or {}
                # SFM returns count = total when limit covers all rows; we
                # query with limit=1 just to be polite, then ask again with
                # a high limit if we need the real total.
                first_count = payload.get("count")
                if first_count is None:
                    overview["stale"][key] = None
                    continue
                # Re-query with high limit to get the true total.
                r2 = await client.get(f"{SFM_BASE_URL}{path}", params={"limit": 100000})
                r2.raise_for_status()
                overview["stale"][key] = (r2.json() or {}).get("count")
            except Exception as e:  # noqa: BLE001
                overview["errors"][f"stale_{key}"] = str(e)

        # Cache stats (in-memory device cache on SFM)
        try:
            r = await client.get(f"{SFM_BASE_URL}/cache/stats")
            r.raise_for_status()
            overview["cache_stats"] = r.json()
        except Exception as e:  # noqa: BLE001
            overview["errors"]["cache_stats"] = str(e)

    # Aggregate counts the UI can render without re-walking arrays
    overview["totals"] = {
        "units":               len(overview["units"]),
        "events_total":        sum(u.get("total_events", 0) for u in overview["units"]),
        "stale_monitor_log":   overview["stale"]["monitor_log"],
        "stale_sessions":      overview["stale"]["sessions"],
    }

    return JSONResponse(overview)


# ── SLMM ──────────────────────────────────────────────────────────────────────


@router.get("/admin/slmm", response_class=HTMLResponse)
def admin_slmm_page(request: Request):
    return templates.TemplateResponse("admin_slmm.html", {
        "request": request,
        "slmm_base_url": SLMM_BASE_URL,
    })


@router.get("/api/admin/slmm/overview")
async def admin_slmm_overview() -> JSONResponse:
    """Aggregated SLMM diagnostic snapshot."""
    overview: Dict[str, Any] = {
        "slmm_base_url": SLMM_BASE_URL,
        "checked_at":    datetime.now(timezone.utc).isoformat(),
        "health":        None,
        "reachable":     False,
        "devices":       [],
        "errors":        {},
    }

    async with httpx.AsyncClient(timeout=5.0) as client:
        try:
            r = await client.get(f"{SLMM_BASE_URL}/health")
            r.raise_for_status()
            overview["health"]    = r.json()
            overview["reachable"] = True
        except Exception as e:  # noqa: BLE001
            overview["errors"]["health"] = str(e)
            return JSONResponse(overview)

        # Pull a roster of configured devices (SLMM exposes per-unit
        # config + status under /api/nl43/*).  This is a best-effort probe
        # — SLMM doesn't expose a "list all devices" endpoint, so we ask
        # terra-view's RosterUnit table what serials it knows about for
        # SLMs and just check each one.  For now, just surface the health
        # payload and let the operator click through to /sound-level-meters
        # for the per-device details.

    return JSONResponse(overview)
