"""pinn 用户管理 API — owner only。"""

from __future__ import annotations

from typing import TYPE_CHECKING, Annotated, Any

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field

from niannian.http.deps import _resolve_bearer_token
from niannian.http.core_auth import require_admin_owner
from niannian.tenant.registry import TenantUserRegistry, validate_username
from niannian.tenant.pending_fetch import (
    ALLOWED_FETCH_RESOURCES,
    PendingFetchStore,
    fetch_push_token_ttl_sec,
)
from niannian.tenant.sessions import TenantSessionStore
from niannian.tenant.tenant_auth_settings import TenantAuthSettings

if TYPE_CHECKING:
    from niannian.client import NianNian


class CreateUserBody(BaseModel):
    username: str = Field(..., min_length=1)


class CloudFetchPolicyPatch(BaseModel):
    cloud_fetch_policy: dict[str, bool] | None = None
    force_reinit_pending: bool | None = None
    force_reinit_init_id: str | None = None


class TenantAuthPatch(BaseModel):
    login_enabled: bool


def create_admin_users_router(nn: NianNian) -> APIRouter:
    router = APIRouter(prefix="/v1/admin", tags=["admin"])
    registry = TenantUserRegistry(nn._home)
    sessions = TenantSessionStore(nn._home)
    auth_settings = TenantAuthSettings(nn._home)
    pending = PendingFetchStore(nn._home)

    @router.get("/users")
    def list_users(_role: Annotated[str, Depends(require_admin_owner)]) -> dict[str, Any]:
        registry.reload()
        return {"users": registry.list_users()}

    @router.post("/users")
    def create_user(
        body: CreateUserBody,
        _role: Annotated[str, Depends(require_admin_owner)],
    ) -> dict[str, Any]:
        try:
            username = validate_username(body.username)
        except ValueError as exc:
            raise HTTPException(
                status_code=400,
                detail={"code": "invalid_username", "message": str(exc)},
            ) from exc
        if username == "pinn":
            raise HTTPException(
                status_code=400,
                detail={"code": "invalid_username", "message": "reserved username"},
            )
        row = registry.ensure_user(username, role="tenant", status="active")
        return {"username": username, **row}

    @router.delete("/users/{username}")
    def delete_user(username: str, _role: Annotated[str, Depends(require_admin_owner)]) -> dict[str, Any]:
        try:
            name = validate_username(username)
            registry.remove_user(name)
        except ValueError as exc:
            raise HTTPException(status_code=400, detail={"code": "forbidden", "message": str(exc)}) from exc
        except KeyError:
            raise HTTPException(
                status_code=404,
                detail={"code": "user_not_found", "message": "user not found"},
            )
        sessions.revoke_user(name)
        return {"username": name, "removed": True}

    @router.patch("/users/{username}")
    def patch_user(
        username: str,
        body: CloudFetchPolicyPatch,
        _role: Annotated[str, Depends(require_admin_owner)],
    ) -> dict[str, Any]:
        try:
            name = validate_username(username)
            out: dict[str, Any] = {"username": name}
            if body.cloud_fetch_policy is not None:
                out["cloud_fetch_policy"] = registry.set_cloud_fetch_policy(name, body.cloud_fetch_policy)
            if body.force_reinit_pending is not None:
                out["bootstrap"] = registry.set_force_reinit(
                    name,
                    pending=body.force_reinit_pending,
                    init_id=body.force_reinit_init_id,
                )
        except ValueError as exc:
            raise HTTPException(status_code=400, detail={"code": "invalid_username", "message": str(exc)}) from exc
        except KeyError:
            raise HTTPException(
                status_code=404,
                detail={"code": "user_not_found", "message": "user not found"},
            )
        return out

    @router.post("/users/{username}/fetch/{resource}")
    def trigger_fetch(
        username: str,
        resource: str,
        _role: Annotated[str, Depends(require_admin_owner)],
    ) -> dict[str, Any]:
        if not auth_settings.login_enabled(nn._config):
            raise HTTPException(
                status_code=403,
                detail={"code": "tenant_login_disabled", "message": "tenant login is disabled"},
            )
        try:
            name = validate_username(username)
        except ValueError as exc:
            raise HTTPException(status_code=400, detail={"code": "invalid_username", "message": str(exc)}) from exc

        resource_key = str(resource).strip()
        if resource_key not in ALLOWED_FETCH_RESOURCES:
            raise HTTPException(
                status_code=400,
                detail={"code": "invalid_resource", "message": f"unsupported resource: {resource_key}"},
            )

        row = registry.get_user(name)
        if row is None or str(row.get("status", "")).lower() != "active":
            raise HTTPException(
                status_code=404,
                detail={"code": "user_not_found", "message": "user not found"},
            )
        if not registry.is_fetch_allowed(name, resource_key):
            raise HTTPException(
                status_code=403,
                detail={"code": "fetch_not_allowed", "message": f"fetch not allowed for {resource_key}"},
            )

        bearer = _resolve_bearer_token(nn._config)
        if not bearer:
            raise HTTPException(
                status_code=503,
                detail={"code": "auth_misconfigured", "message": "NIANNIAN_API_TOKEN required to issue push_token"},
            )

        ttl = fetch_push_token_ttl_sec(nn._config)
        created = pending.create_pending(
            username=name,
            resource=resource_key,
            bearer_secret=bearer,
            ttl_sec=ttl,
        )
        return created

    @router.get("/users/{username}/fetch/{fetch_id}")
    def get_fetch_status(
        username: str,
        fetch_id: str,
        _role: Annotated[str, Depends(require_admin_owner)],
    ) -> dict[str, Any]:
        try:
            name = validate_username(username)
        except ValueError as exc:
            raise HTTPException(status_code=400, detail={"code": "invalid_username", "message": str(exc)}) from exc

        row = pending.get(fetch_id)
        if row is None or str(row.get("username", "")).lower() != name:
            raise HTTPException(
                status_code=404,
                detail={"code": "fetch_not_found", "message": "fetch not found"},
            )

        resource = str(row.get("resource", ""))
        status = str(row.get("status", "pending"))
        if status == "timeout":
            try:
                registry.record_fetch_status(name, status="timeout")
            except KeyError:
                pass
        out: dict[str, Any] = {
            "fetch_id": fetch_id,
            "username": name,
            "resource": resource,
            "status": status,
            "created_at": row.get("created_at"),
            "expires_at": row.get("expires_at"),
        }
        if status == "ok":
            preview = pending.read_cache_preview(name, resource)
            if preview is not None:
                out["cache"] = preview
        return out

    @router.get("/tenant-auth")
    def get_tenant_auth(_role: Annotated[str, Depends(require_admin_owner)]) -> dict[str, Any]:
        return {"login_enabled": auth_settings.login_enabled(nn._config)}

    @router.patch("/tenant-auth")
    def patch_tenant_auth(
        body: TenantAuthPatch,
        _role: Annotated[str, Depends(require_admin_owner)],
    ) -> dict[str, Any]:
        enabled = auth_settings.set_login_enabled(body.login_enabled)
        return {"login_enabled": enabled}

    return router
