"""Admin API 共享依赖:DB session、当前 admin、角色守卫、客户端 IP。

仿 app/api/deps.py 的 get_current_user,但验的是独立的 admin JWT、查的是 admin_user 表。
"""
from __future__ import annotations

from typing import Annotated

from fastapi import Depends, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.orm import Session

from app.admin.permissions import ALL_PAGE_KEYS, CUSTOM_ROLE, SUPER_ADMIN_ROLE, sanitize_pages
from app.admin.repositories import admin_role as role_repo
from app.admin.repositories import admin_user as admin_repo
from app.admin.security import AdminTokenError, decode_admin_token
from app.db.session import get_db
from app.models.admin import AdminUser

_bearer = HTTPBearer(auto_error=False, scheme_name="AdminBearer")


def get_current_admin(
    credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(_bearer)],
    db: Annotated[Session, Depends(get_db)],
) -> AdminUser:
    """从 Authorization: Bearer <admin_token> 解出当前管理员。失败统一 401。"""
    if credentials is None or credentials.scheme.lower() != "bearer":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="missing bearer token",
            headers={"WWW-Authenticate": "Bearer"},
        )
    try:
        payload = decode_admin_token(credentials.credentials)
    except AdminTokenError as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(e),
            headers={"WWW-Authenticate": "Bearer"},
        ) from e

    admin = admin_repo.get_by_id(db, int(payload["sub"]))
    if admin is None or admin.status != "active":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="admin not found or disabled",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return admin


CurrentAdmin = Annotated[AdminUser, Depends(get_current_admin)]
AdminDb = Annotated[Session, Depends(get_db)]


def require_role(*roles: str):
    """角色守卫依赖工厂。super_admin 恒通过(全权)。

    用法:在路由签名加 `_: Annotated[AdminUser, Depends(require_role("finance"))]`,
    或 `dependencies=[Depends(require_role("finance"))]`(不需要拿 admin 时)。
    """
    allowed = set(roles)

    def _checker(admin: CurrentAdmin) -> AdminUser:
        if admin.role != "super_admin" and admin.role not in allowed:
            need = sorted(allowed | {"super_admin"})
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"role '{admin.role}' not allowed (need one of {need})",
            )
        return admin

    return _checker


def require_page(page: str):
    """页面权限守卫依赖工厂。

    左侧导航隐藏只是 UI，这个守卫确保直接调用 API 也必须持有对应页面权限。
    super_admin 恒通过；custom 读个人 pages_override；其余角色读 admin_role.pages。
    """
    if page not in ALL_PAGE_KEYS:
        raise ValueError(f"unknown admin page permission: {page}")

    def _checker(admin: CurrentAdmin, db: AdminDb) -> AdminUser:
        if admin.role == SUPER_ADMIN_ROLE:
            return admin
        pages = (
            sanitize_pages(admin.pages_override)
            if admin.role == CUSTOM_ROLE
            else role_repo.effective_pages_of(db, admin.role)
        )
        if page not in pages:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"page '{page}' not allowed",
            )
        return admin

    return _checker


def get_client_ip(request: Request) -> str:
    """取客户端 IP(审计日志用)。生产经 nginx 反代,优先 X-Forwarded-For 第一段;否则直连 IP。

    ⚠️ XFF 可被客户端伪造。nginx 必须用 `proxy_set_header X-Forwarded-For $remote_addr`
    覆盖客户端传入值(见 M4 部署),否则审计里的 IP 可被伪造。审计 IP 仅作记录、不参与鉴权。
    """
    xff = request.headers.get("x-forwarded-for")
    if xff:
        return xff.split(",")[0].strip()
    return request.client.host if request.client else ""
