"""Admin endpoints: users, roles, audit-logs, system, tokens."""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Any

from fastapi import APIRouter, HTTPException, Query, Request

from api.deps import (
    _actor,
    _cleanup_expired_tokens,
    _ensure_admin_seed,
    _env_int,
    _issue_token_record,
    _new_id,
    _now_iso,
    _paginate,
    _parse_datetime,
    _record_audit,
    _require_role,
    _rotate_token_record,
    _token_is_expiring,
    _token_public_view,
)
from api.models import (
    AdminTokenCreateRequest,
    AdminUserCreateRequest,
    AdminUserRolesRequest,
    AdminUserUpdateRequest,
    ReloadConfigRequest,
)
from api.state import (
    BOOT_TS,
    ROLE_LEVEL,
    ROLE_PERMISSIONS,
    STATE_LOCK,
    TOKENS,
    USERS,
)
from src.bootstrap_config import apply_initial_env_from_config
from src.env_settings import load_env_candidates
from src.user_store import (
    count_audit_logs,
    count_users,
    get_langfuse_settings_encryption_status,
    list_audit_logs as db_list_audit_logs,
    migrate_user_langfuse_settings_encryption,
)

router = APIRouter()


# ---------------------------------------------------------------------------
# Users
# ---------------------------------------------------------------------------

@router.get("/api/admin/users")
def list_admin_users(
    request: Request,
    q: str | None = None,
    role: str | None = None,
    status: str | None = None,
    page: int = 1,
    size: int = 20,
) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _ensure_admin_seed()

    with STATE_LOCK:
        users = list(USERS.values())

    def _match(item: dict[str, Any]) -> bool:
        if q and q.lower() not in f"{item.get('name', '')} {item.get('email', '')}".lower():
            return False
        if role and role not in item.get("roles", []):
            return False
        if status and item.get("status") != status:
            return False
        return True

    filtered = [item for item in users if _match(item)]
    filtered.sort(key=lambda u: u.get("created_at") or "", reverse=True)
    return {"ok": True, "data": _paginate(filtered, page, size)}


@router.post("/api/admin/users")
def create_admin_user(payload: AdminUserCreateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _ensure_admin_seed()

    invalid_roles = [role for role in payload.roles if role not in ROLE_LEVEL]
    if invalid_roles:
        raise HTTPException(status_code=400, detail=f"invalid roles: {', '.join(invalid_roles)}")

    user_id = _new_id("user")
    user = {
        "user_id": user_id,
        "email": payload.email,
        "name": payload.name,
        "roles": payload.roles,
        "status": "active",
        "created_at": _now_iso(),
        "updated_at": _now_iso(),
    }
    with STATE_LOCK:
        USERS[user_id] = user

    _record_audit(request, action="admin.user.create", resource_type="user", resource_id=user_id)
    return {"ok": True, "data": user}


@router.patch("/api/admin/users/{user_id}")
def update_admin_user(user_id: str, payload: AdminUserUpdateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _ensure_admin_seed()

    with STATE_LOCK:
        user = USERS.get(user_id)
        if not user:
            raise HTTPException(status_code=404, detail="user not found")
        if payload.status is not None:
            user["status"] = payload.status
        if payload.name is not None:
            user["name"] = payload.name
        user["updated_at"] = _now_iso()

    _record_audit(request, action="admin.user.update", resource_type="user", resource_id=user_id)
    return {"ok": True, "data": user}


@router.put("/api/admin/users/{user_id}/roles")
def update_admin_user_roles(user_id: str, payload: AdminUserRolesRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _ensure_admin_seed()

    invalid_roles = [role for role in payload.roles if role not in ROLE_LEVEL]
    if invalid_roles:
        raise HTTPException(status_code=400, detail=f"invalid roles: {', '.join(invalid_roles)}")

    with STATE_LOCK:
        user = USERS.get(user_id)
        if not user:
            raise HTTPException(status_code=404, detail="user not found")
        user["roles"] = payload.roles
        user["updated_at"] = _now_iso()

    _record_audit(request, action="admin.user.roles.update", resource_type="user", resource_id=user_id)
    return {"ok": True, "data": user}


@router.get("/api/admin/roles")
def get_admin_roles(request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    items = [{"name": name, "level": ROLE_LEVEL[name], "permissions": permissions} for name, permissions in ROLE_PERMISSIONS.items()]
    items.sort(key=lambda i: i["level"])
    return {"ok": True, "data": {"roles": items}}


# ---------------------------------------------------------------------------
# Audit logs
# ---------------------------------------------------------------------------

@router.get("/api/admin/audit-logs")
def get_audit_logs(
    request: Request,
    page: int = 1,
    size: int = 20,
) -> dict[str, Any]:
    _require_role(request, "super_admin")
    safe_page = max(1, int(page))
    safe_size = max(1, min(int(size), 100))
    offset = (safe_page - 1) * safe_size
    total = count_audit_logs()
    items = db_list_audit_logs(limit=safe_size, offset=offset)
    return {
        "ok": True,
        "data": {
            "page": safe_page,
            "size": safe_size,
            "total": total,
            "items": items,
        },
    }


# ---------------------------------------------------------------------------
# System
# ---------------------------------------------------------------------------

@router.get("/api/admin/system/health")
def admin_system_health(request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    from src.user_store import count_chat_threads

    now = datetime.now(timezone.utc)
    uptime_sec = int((now - BOOT_TS).total_seconds())

    with STATE_LOCK:
        user_count = len(USERS)
    sqlite_user_count = count_users()
    encryption = get_langfuse_settings_encryption_status()

    return {
        "ok": True,
        "data": {
            "api_ok": True,
            "queue_ok": True,
            "db_ok": True,
            "version": request.app.version,
            "uptime_sec": uptime_sec,
            "counts": {
                "chats": count_chat_threads(),
                "audit_logs": count_audit_logs(),
                "legacy_users": user_count,
                "sqlite_users": sqlite_user_count,
            },
            "encryption": encryption,
        },
    }


@router.post("/api/admin/system/reload-config")
def admin_reload_config(payload: ReloadConfigRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    load_env_candidates()
    apply_initial_env_from_config(force=True)
    _record_audit(
        request,
        action="admin.system.reload_config",
        resource_type="system",
        resource_id=payload.scope,
        detail={"scope": payload.scope},
    )
    return {"ok": True, "data": {"scope": payload.scope, "reloaded_at": _now_iso()}}


@router.get("/api/admin/system/langfuse-settings-encryption")
def admin_langfuse_settings_encryption_status(request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    try:
        data = get_langfuse_settings_encryption_status()
    except Exception as exc:
        import logging; logging.getLogger(__name__).exception("encryption status check failed")
        raise HTTPException(status_code=500, detail="encryption status check failed")
    return {"ok": True, "data": data}


@router.post("/api/admin/system/langfuse-settings-encryption/migrate")
def admin_migrate_langfuse_settings_encryption(
    request: Request,
    dry_run: bool = Query(default=False),
) -> dict[str, Any]:
    _require_role(request, "super_admin")
    try:
        data = migrate_user_langfuse_settings_encryption(dry_run=dry_run)
    except Exception as exc:
        import logging; logging.getLogger(__name__).exception("encryption migration failed")
        raise HTTPException(status_code=500, detail="encryption migration failed")
    _record_audit(
        request,
        action="admin.system.langfuse_settings_encryption.migrate",
        resource_type="system",
        resource_id="user_langfuse_settings",
        detail={"dry_run": bool(dry_run), "affected_rows": data.get("affected_rows")},
    )
    return {"ok": True, "data": data}


# ---------------------------------------------------------------------------
# Tokens
# ---------------------------------------------------------------------------

@router.post("/api/admin/tokens")
def create_admin_token(payload: AdminTokenCreateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    default_expires_days = _env_int("AGENT_TOKEN_DEFAULT_EXPIRES_DAYS", 30, 1, 3650)
    default_rotate_before_days = _env_int("AGENT_TOKEN_DEFAULT_ROTATE_BEFORE_DAYS", 3, 1, 3650)
    expires_days = int(payload.expires_days or default_expires_days)
    rotate_before_days = int(payload.rotate_before_days or default_rotate_before_days)
    record, raw_token = _issue_token_record(
        name=payload.name,
        role=payload.role,
        scopes=payload.scopes,
        subject=payload.subject,
        expires_days=expires_days,
        created_by=_actor(request),
        auto_rotate=payload.auto_rotate,
        rotate_before_days=rotate_before_days,
    )
    _record_audit(request, action="admin.token.create", resource_type="token", resource_id=record["token_id"])
    return {
        "ok": True,
        "data": {
            "token_id": record["token_id"],
            "token": raw_token,
            "name": record["name"],
            "role": record["role"],
            "scopes": record["scopes"],
            "subject": record["subject"],
            "expires_at": record["expires_at"],
            "auto_rotate": record["auto_rotate"],
            "rotate_before_days": record["rotate_before_days"],
        },
    }


@router.get("/api/admin/tokens")
def list_admin_tokens(request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _cleanup_expired_tokens()
    with STATE_LOCK:
        items = []
        for token in TOKENS.values():
            items.append(_token_public_view(token))
    items.sort(key=lambda item: item.get("created_at") or "", reverse=True)
    return {"ok": True, "data": {"items": items, "total": len(items)}}


@router.post("/api/admin/tokens/{token_id}/rotate")
def rotate_admin_token(token_id: str, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    rotated = _rotate_token_record(token_id, rotated_by=_actor(request), force=True)
    _record_audit(
        request,
        action="admin.token.rotate",
        resource_type="token",
        resource_id=token_id,
        detail={"new_token_id": rotated["new_record"]["token_id"]},
    )
    return {"ok": True, "data": rotated}


@router.post("/api/admin/tokens/rotate-expiring")
def rotate_expiring_admin_tokens(
    request: Request,
    within_days: int | None = None,
    dry_run: bool = False,
) -> dict[str, Any]:
    _require_role(request, "super_admin")
    _cleanup_expired_tokens()
    default_within = _env_int("AGENT_TOKEN_DEFAULT_ROTATE_BEFORE_DAYS", 3, 1, 3650)
    within = max(0, min(int(within_days if within_days is not None else default_within), 3650))
    rotations: list[dict[str, Any]] = []

    with STATE_LOCK:
        candidate_ids = [token_id for token_id, token in TOKENS.items() if _token_is_expiring(token, within)]

    for token_id in candidate_ids:
        with STATE_LOCK:
            snapshot = TOKENS.get(token_id, {})
            if not snapshot or snapshot.get("revoked"):
                continue
            if not bool(snapshot.get("auto_rotate", True)):
                continue
        if dry_run:
            rotations.append({"old_token_id": token_id, "dry_run": True})
            continue
        try:
            rotated = _rotate_token_record(token_id, rotated_by=_actor(request), force=False, within_days=within)
            rotations.append(rotated)
        except HTTPException:
            continue

    _record_audit(
        request,
        action="admin.token.rotate_expiring",
        resource_type="token",
        resource_id="bulk",
        detail={"count": len(rotations), "within_days": within, "dry_run": dry_run},
    )
    return {"ok": True, "data": {"rotations": rotations, "count": len(rotations), "dry_run": dry_run}}


@router.delete("/api/admin/tokens/{token_id}")
def revoke_admin_token(token_id: str, request: Request) -> dict[str, Any]:
    _require_role(request, "super_admin")
    with STATE_LOCK:
        token = TOKENS.get(token_id)
        if not token:
            raise HTTPException(status_code=404, detail="token not found")
        token["revoked"] = True
        token["revoked_at"] = _now_iso()

    _record_audit(request, action="admin.token.revoke", resource_type="token", resource_id=token_id)
    return {"ok": True, "data": {"token_id": token_id, "revoked": True}}
