"""Federation cockpit routes — admin page, public feed/key/info endpoints.

Wired into the FastAPI app by app.py via a single `register(app, TEMPLATES)`
call so the federation surface stays self-contained.
"""

from __future__ import annotations

import json
import time
from typing import Any, Dict, Optional, Tuple

from fastapi import FastAPI, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse
from fastapi.templating import Jinja2Templates

from psyc import db, log
from psyc.lines import federation


_log = log.get(__name__)

# Tiny in-memory cache for the signed feed — peers may poll, recomputing
# canonical JSON + signature on every hit would be wasteful.
_FEED_CACHE: Dict[str, Any] = {"ts": 0.0, "payload": None}
_FEED_TTL = 60.0


def _admin_ok(request: Request) -> bool:
    return bool(request.session.get("admin_ok"))


def _cached_feed() -> Dict[str, Any]:
    now = time.time()
    if _FEED_CACHE["payload"] is None or (now - _FEED_CACHE["ts"]) > _FEED_TTL:
        _FEED_CACHE["payload"] = federation.build_signed_feed()
        _FEED_CACHE["ts"] = now
    return _FEED_CACHE["payload"]


def register(app: FastAPI, TEMPLATES: Jinja2Templates) -> None:
    """Mount all federation routes onto `app`."""

    @app.get("/admin/federation", response_class=HTMLResponse)
    def admin_federation(request: Request) -> HTMLResponse:
        if not _admin_ok(request):
            return RedirectResponse("/admin", status_code=303)
        host = request.url.hostname or "your-node.example"
        suggested = request.query_params.get("domain", host)
        rec = federation.dns_record(suggested)
        peers = federation.list_peers()
        signals = db.recent_signals(limit=20)
        return TEMPLATES.TemplateResponse(
            request,
            "admin_federation.html",
            {
                "fingerprint": federation.node_fingerprint(),
                "pubkey_pem": federation.public_key_pem(),
                "suggested_domain": suggested,
                "dns": rec,
                "peers": peers,
                "signals": signals,
            },
        )

    @app.post("/admin/federation/peers/add")
    def admin_federation_add_peer(
        request: Request,
        domain: str = Form(...),
        fingerprint: str = Form(...),
        pubkey_pem: str = Form(...),
        status: str = Form("unknown"),
    ) -> RedirectResponse:
        if not _admin_ok(request):
            raise HTTPException(status_code=403, detail="admin session required")
        try:
            federation.register_peer(domain.strip(), fingerprint.strip(), pubkey_pem.strip(), status=status)
        except Exception as exc:
            _log.warning("federation.peer.add.error", domain=domain, error=str(exc))
        return RedirectResponse("/admin/federation", status_code=303)

    @app.post("/admin/federation/peers/{domain}/status")
    def admin_federation_set_status(
        request: Request,
        domain: str,
        status: str = Form(...),
    ) -> RedirectResponse:
        if not _admin_ok(request):
            raise HTTPException(status_code=403, detail="admin session required")
        try:
            federation.set_peer_status(domain, status)
        except ValueError as exc:
            _log.warning("federation.peer.status.bad", domain=domain, status=status, error=str(exc))
        return RedirectResponse("/admin/federation", status_code=303)

    @app.post("/admin/federation/peers/{domain}/remove")
    def admin_federation_remove(
        request: Request,
        domain: str,
    ) -> RedirectResponse:
        if not _admin_ok(request):
            raise HTTPException(status_code=403, detail="admin session required")
        federation.remove_peer(domain)
        return RedirectResponse("/admin/federation", status_code=303)

    # ---------- public endpoints --------------------------------------

    @app.get("/federation/info")
    def federation_info() -> JSONResponse:
        return JSONResponse({
            "fingerprint": federation.node_fingerprint(),
            "version": federation.FEED_VERSION,
            "feed": federation.FEED_PATH,
            "key": "/federation/key",
        })

    @app.get("/federation/key", response_class=PlainTextResponse)
    def federation_key() -> PlainTextResponse:
        return PlainTextResponse(federation.public_key_pem(), media_type="text/plain")

    @app.get("/federation/feed")
    def federation_feed() -> JSONResponse:
        return JSONResponse(_cached_feed())

    _log.info("federation.routes.registered")
