"""Admin-only audit log query API (GET /api/v1/admin/audit)."""

from __future__ import annotations

import uuid
from datetime import datetime, timedelta, timezone
from typing import Annotated, Any, Optional

from fastapi import APIRouter, Header, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy import asc, desc, func, select

from src.auth_jwt import decode_access_token_user_id, decode_bearer_token
from src.initiative_db.engine import get_session, init_engine, is_postgres_enabled
from src.initiative_db.models import AuditEvent

router = APIRouter(prefix="/admin", tags=["admin-audit"])


def _jwt_role_strings(authorization: str | None) -> list[str]:
    p = decode_bearer_token(authorization)
    if not p:
        return []
    r = p.get("roles")
    if isinstance(r, list):
        return [str(x) for x in r]
    return []


def require_admin_uid(authorization: str | None) -> uuid.UUID:
    uid = decode_access_token_user_id(authorization)
    if uid is None:
        raise HTTPException(status_code=401, detail="Đăng nhập để thực hiện thao tác.")
    if "admin" not in _jwt_role_strings(authorization):
        raise HTTPException(status_code=403, detail="Chỉ tài khoản quản trị mới thực hiện được.")
    return uid


class AuditEventListItem(BaseModel):
    model_config = {"from_attributes": True}

    id: int
    occurred_at: datetime
    actor_user_id: Optional[uuid.UUID] = None
    actor_email: str
    actor_role: str
    action: str
    entity_type: str
    entity_id: Optional[str] = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    request_id: Optional[uuid.UUID] = None
    has_before: bool = False
    has_after: bool = False


class AuditListResponse(BaseModel):
    items: list[AuditEventListItem]
    total: int
    page: int
    page_size: int


class AuditEventDetail(BaseModel):
    id: int
    occurred_at: datetime
    actor_user_id: Optional[uuid.UUID] = None
    actor_email: str
    actor_role: str
    action: str
    entity_type: str
    entity_id: Optional[str] = None
    before: Optional[dict[str, Any]] = None
    after: Optional[dict[str, Any]] = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    request_id: Optional[uuid.UUID] = None


_AUDIT_ACTIONS = frozenset(
    {"create", "read", "update", "delete", "login", "logout", "login_failed"}
)


def _parse_sort(sort: str) -> bool:
    """True when sorting occurred_at ascending."""
    s = (sort or "occurred_at:desc").strip().lower()
    if ":" in s:
        col_name, direction = s.split(":", 1)
    else:
        col_name, direction = s, "desc"
    if col_name != "occurred_at":
        raise HTTPException(status_code=400, detail='sort chỉ hỗ trợ occurred_at (+ asc|desc)')
    return direction in ("asc", "ascending", "old", "older")


def _where_audit(
    *,
    from_ts: datetime,
    to_ts: datetime,
    actor_user_id: Optional[uuid.UUID],
    actor_email: Optional[str],
    entity_type: Optional[str],
    entity_id: Optional[str],
    actions: Optional[list[str]],
    request_id: Optional[uuid.UUID],
):
    parts = [
        AuditEvent.occurred_at >= from_ts,
        AuditEvent.occurred_at <= to_ts,
    ]
    if actor_user_id is not None:
        parts.append(AuditEvent.actor_user_id == actor_user_id)
    if actor_email:
        parts.append(AuditEvent.actor_email == actor_email.strip().lower())
    if entity_type:
        parts.append(AuditEvent.entity_type == entity_type.strip())
    if entity_id is not None and entity_id.strip() != "":
        parts.append(AuditEvent.entity_id == entity_id.strip())
    if actions:
        parts.append(AuditEvent.action.in_(actions))
    if request_id is not None:
        parts.append(AuditEvent.request_id == request_id)
    return parts


@router.get("/audit", response_model=AuditListResponse)
async def list_audit_events(
    authorization: Annotated[str | None, Header()] = None,
    from_: Annotated[
        Optional[datetime],
        Query(alias="from", description="Inclusive lower bound (UTC). Default: now−7d"),
    ] = None,
    to: Annotated[
        Optional[datetime],
        Query(description="Inclusive upper bound (UTC). Default: now"),
    ] = None,
    actor_user_id: Optional[uuid.UUID] = None,
    actor_email: Optional[str] = None,
    entity_type: Optional[str] = None,
    entity_id: Optional[str] = None,
    action: Optional[str] = Query(
        None, description="Comma-separated audit_action values"
    ),
    request_id: Optional[uuid.UUID] = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=100),
    sort: str = Query("occurred_at:desc", description='e.g. "occurred_at:desc"'),
):
    require_admin_uid(authorization)
    if not is_postgres_enabled():
        raise HTTPException(status_code=503, detail="Cần PostgreSQL để đọc audit.")

    await init_engine()
    now = datetime.now(timezone.utc)
    end = to or now
    start = from_ or (end - timedelta(days=7))
    if end < start:
        raise HTTPException(status_code=400, detail="Tham số to phải >= from")

    actions_list: Optional[list[str]] = None
    if action:
        raw = [a.strip().lower() for a in action.split(",") if a.strip()]
        bad = [a for a in raw if a not in _AUDIT_ACTIONS]
        if bad:
            raise HTTPException(status_code=400, detail=f"action không hợp lệ: {bad}")
        actions_list = raw

    asc_order = _parse_sort(sort)
    offset = (page - 1) * page_size
    wh = _where_audit(
        from_ts=start,
        to_ts=end,
        actor_user_id=actor_user_id,
        actor_email=actor_email,
        entity_type=entity_type,
        entity_id=entity_id,
        actions=actions_list,
        request_id=request_id,
    )

    async with get_session() as session:
        cnt_stmt = select(func.count()).select_from(AuditEvent).where(*wh)
        total = int((await session.execute(cnt_stmt)).scalar_one())

        order_clause = asc(AuditEvent.occurred_at) if asc_order else desc(AuditEvent.occurred_at)
        stmt = select(AuditEvent).where(*wh).order_by(order_clause).limit(page_size).offset(offset)
        rows = (await session.execute(stmt)).scalars().all()

    items = [
        AuditEventListItem(
            id=r.id,
            occurred_at=r.occurred_at,
            actor_user_id=r.actor_user_id,
            actor_email=r.actor_email,
            actor_role=r.actor_role,
            action=str(r.action),
            entity_type=r.entity_type,
            entity_id=r.entity_id,
            metadata=dict(r.metadata_) if isinstance(r.metadata_, dict) else {},
            request_id=r.request_id,
            has_before=r.before is not None,
            has_after=r.after is not None,
        )
        for r in rows
    ]
    return AuditListResponse(items=items, total=total, page=page, page_size=page_size)


@router.get("/audit/{event_id:int}", response_model=AuditEventDetail)
async def get_audit_event_detail(
    event_id: int,
    authorization: Annotated[str | None, Header()] = None,
):
    require_admin_uid(authorization)
    if not is_postgres_enabled():
        raise HTTPException(status_code=503, detail="Cần PostgreSQL để đọc audit.")

    await init_engine()
    async with get_session() as session:
        row = await session.get(AuditEvent, event_id)
        if row is None:
            raise HTTPException(status_code=404, detail="Không có sự kiện audit.")
        return AuditEventDetail(
            id=row.id,
            occurred_at=row.occurred_at,
            actor_user_id=row.actor_user_id,
            actor_email=row.actor_email,
            actor_role=row.actor_role,
            action=str(row.action),
            entity_type=row.entity_type,
            entity_id=row.entity_id,
            before=dict(row.before) if isinstance(row.before, dict) else row.before,
            after=dict(row.after) if isinstance(row.after, dict) else row.after,
            metadata=dict(row.metadata_) if isinstance(row.metadata_, dict) else {},
            request_id=row.request_id,
        )
