"""Central config endpoints — the hive tells this instance that config changed.

The hive does not push the document itself. It posts ``{"version", "hash"}``
to every registered instance; each instance fetches, validates and applies the
document on its own, which keeps the instance in control of what it runs and
lets a machine that was offline catch up on its next start (or on its next
announce reply).

``POST /config/notify`` accepts a request from anyone holding the swarm key.
That key is the fleet's read credential and it is enough here: a notification
causes a *fetch* against the hive, so a forged one can at most make this
instance re-read the config it would have read anyway.
"""

from __future__ import annotations

import asyncio

import structlog
from fastapi import APIRouter, Header, HTTPException
from pydantic import BaseModel

from navi.overlay import refresh, status_dict
from navi.overlay.state import get_state

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

log = structlog.get_logger()


def _require_swarm_key(x_swarm_key: str | None) -> None:
    from navi.swarm import verify_swarm_key

    if not x_swarm_key:
        raise HTTPException(status_code=401, detail="X-Swarm-Key header required")
    if not verify_swarm_key(x_swarm_key):
        raise HTTPException(status_code=403, detail="invalid swarm key")


class NotifyPayload(BaseModel):
    version: int | None = None
    hash: str | None = None


@router.post("/notify", status_code=202)
async def config_notify(
    payload: NotifyPayload | None = None,
    x_swarm_key: str | None = Header(None),
) -> dict:
    """Schedule a refresh; answer at once so the hive's fan-out stays fast."""
    _require_swarm_key(x_swarm_key)
    state = get_state()
    if not state.enabled or state.base_settings is None:
        return {"status": state.status, "detail": "central config disabled on this instance"}

    notified = payload or NotifyPayload()
    log.info(
        "hive.config.notified",
        version=notified.version,
        hash=notified.hash,
        current_hash=state.hash,
    )
    # Blocking HTTP inside the refresh — run it off the event loop.
    asyncio.create_task(asyncio.to_thread(refresh, "notify"))
    return {
        "status": "scheduled",
        "version": notified.version,
        "hash": notified.hash,
        "current": status_dict()["status"],
    }


@router.get("/status")
async def config_status(x_swarm_key: str | None = Header(None)) -> dict:
    """What this instance currently runs — the same block /health shows."""
    _require_swarm_key(x_swarm_key)
    return status_dict()
