# -*- coding: utf-8 -*-
"""接口层 RBAC 守卫工厂：角色 / 权限 / 数据范围守卫 + 审计写入。

判定语义在领域层 ``domain/rules.py``；本文件只把规则映射为 HTTP 异常。
"""
from __future__ import annotations

from collections.abc import Callable

from fastapi import Depends, HTTPException, Request

from .api.dependencies import get_current_user
from .domain.rules import (
    is_government,
    permission_granted,
    region_in_scope,
    role_allowed,
    scope_level_ok,
    sub_role_allowed,
)
from .infrastructure.repositories import Database


def require_roles(*roles: str) -> Callable:
    """要求当前用户业务角色 ∈ roles；否则 403。"""
    def dep(user: dict = Depends(get_current_user)) -> dict:
        if not role_allowed(user, *roles):
            raise HTTPException(status_code=403, detail="Forbidden: insufficient role")
        return user
    return dep


def require_sub_roles(*sub_roles: str) -> Callable:
    """要求当前用户子角色 ∈ sub_roles（如 op_super_admin）；否则 403。"""
    def dep(user: dict = Depends(get_current_user)) -> dict:
        if not sub_role_allowed(user, *sub_roles):
            raise HTTPException(status_code=403, detail="Forbidden: insufficient sub-role")
        return user
    return dep


def require_permission(perm: str) -> Callable:
    """要求当前用户拥有权限码 ``perm``；否则 403。"""
    def dep(user: dict = Depends(get_current_user)) -> dict:
        if not permission_granted(user, perm):
            raise HTTPException(status_code=403, detail=f"Forbidden: missing permission {perm}")
        return user
    return dep


def require_scope(min_level: str | None = None) -> Callable:
    """政务数据范围守卫：要求 government 且区域层级不低于 min_level。"""
    def dep(user: dict = Depends(get_current_user)) -> dict:
        if not is_government(user):
            raise HTTPException(status_code=403, detail="Forbidden: not government")
        if not user.get("region_id"):
            raise HTTPException(status_code=403, detail="Forbidden: no region scope")
        if not scope_level_ok(user, min_level):
            raise HTTPException(status_code=403, detail=f"Forbidden: scope below {min_level}")
        return user
    return dep


def scope_covers(region_id: str | None) -> Callable:
    """要求 ``region_id`` 落在当前用户数据范围内；否则 403。"""
    def dep(user: dict = Depends(get_current_user)) -> dict:
        if not region_in_scope(user, region_id):
            raise HTTPException(status_code=403, detail="Forbidden: out of data scope")
        return user
    return dep


async def write_audit(
    db: Database,
    *,
    action: str,
    resource: str,
    resource_id: str = "",
    detail: str = "",
    user: dict | None = None,
    request: Request | None = None,
) -> None:
    """写入一条审计日志（异步）。"""
    ip = request.client.host if request is not None and request.client else ""
    await db.audit.add(
        action=action,
        resource=resource,
        resource_id=resource_id,
        detail=detail,
        ip=ip,
        user_id=(user or {}).get("id"),
    )
