"""Administrative API for managing Takumi API keys."""

from __future__ import annotations

from typing import Any

from aiohttp import web

from ..app_keys import PG_POOL_KEY, REQUIRE_ADMIN_KEY, SETTINGS_KEY
from ..auth.service import ApiKeyService, CreateKeyRequest
from ..middleware.context import get_request_context


async def create_key(request: web.Request) -> web.Response:
    _require_admin(request)
    payload = await _read_json(request)
    body = CreateKeyRequest(
        org_id=_expect_str(payload, "org_id"),
        tier=payload.get("tier") or _default_tier(request),
        role=payload.get("role") or "user",
        created_by=_principal_id(request),
        expires_at=payload.get("expires_at"),
    )
    svc = _service(request)
    result = await svc.create(body)
    return web.json_response(result, status=201)


async def list_keys(request: web.Request) -> web.Response:
    _require_admin(request)
    org_id = request.match_info.get("org_id")
    if not org_id:
        raise web.HTTPBadRequest(text="org_id missing")
    svc = _service(request)
    result = await svc.list(org_id)
    return web.json_response(result)


async def set_status(request: web.Request) -> web.Response:
    _require_admin(request)
    api_key_id = request.match_info.get("api_key_id")
    if not api_key_id:
        raise web.HTTPBadRequest(text="api_key_id missing")
    payload = await _read_json(request)
    status = _expect_str(payload, "status")
    svc = _service(request)
    result = await svc.set_status(api_key_id, status)
    return web.json_response(result)


async def rotate_key(request: web.Request) -> web.Response:
    _require_admin(request)
    api_key_id = request.match_info.get("api_key_id")
    if not api_key_id:
        raise web.HTTPBadRequest(text="api_key_id missing")
    svc = _service(request)
    result = await svc.rotate(api_key_id, actor=_principal_id(request))
    return web.json_response(result, status=201)


def register(app: web.Application) -> None:
    """Attach admin API-key routes to the application."""

    app.router.add_post("/v1/admin/api-keys", create_key)
    app.router.add_get("/v1/admin/api-keys/{org_id}", list_keys)
    app.router.add_post("/v1/admin/api-keys/{api_key_id}/status", set_status)
    app.router.add_post("/v1/admin/api-keys/{api_key_id}/rotate", rotate_key)


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _service(request: web.Request) -> ApiKeyService:
    pool = request.app.get(PG_POOL_KEY)
    if pool is None:
        raise web.HTTPServiceUnavailable(text="Postgres pool unavailable")

    settings = request.app.get(SETTINGS_KEY)
    if settings is None:
        raise web.HTTPServiceUnavailable(text="Settings unavailable")

    pepper = getattr(settings.auth, "pepper", "")
    key_prefix = getattr(settings.auth, "key_prefix", "tkm_sk")
    return ApiKeyService(pool, pepper, key_prefix)


def _require_admin(request: web.Request) -> None:
    checker = request.app.get(REQUIRE_ADMIN_KEY)
    if callable(checker):
        checker(request)
    else:
        raise web.HTTPForbidden(text="Admin guard not configured")


async def _read_json(request: web.Request) -> dict[str, Any]:
    body = request.get("json_body")
    if body is None:
        try:
            body = await request.json()
        except (ValueError, TypeError) as exc:
            raise web.HTTPBadRequest(text="Could not parse JSON body") from exc
    if not isinstance(body, dict):
        raise web.HTTPBadRequest(text="Expected JSON object body")
    return body


def _expect_str(payload: dict[str, Any], key: str) -> str:
    value = payload.get(key)
    if not isinstance(value, str) or not value:
        raise web.HTTPBadRequest(text=f"Missing or invalid field: {key}")
    return value


def _principal_id(request: web.Request) -> str | None:
    ctx = get_request_context(request)
    return ctx.api_key_id


def _default_tier(request: web.Request) -> str:
    settings = request.app.get(SETTINGS_KEY)
    if settings is None:
        return "T1"
    return getattr(settings.auth, "default_tier", "T1")
