# =============================================================================
# 企微IT智能服务台 — 管理后台角色管理 API
# =============================================================================
# 说明：管理后台的角色管理接口
# 包含：
#   1. 角色管理（CRUD）
#   2. 用户角色分配/撤销
#   3. 角色映射规则管理
# 所有接口需要 admin 角色权限
# =============================================================================

import logging
from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, Depends, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from app.dependencies import get_current_user, UserInfo, require_role
from app.database import get_db
from app.models.role import Role
from app.models.role_mapping_rule import RoleMappingRule
from app.models.user_role import UserRole
from app.schemas.role import (
    RoleAssignRequest,
    RoleMappingRuleRequest,
    RoleMappingRuleResponse,
    RoleRevokeRequest,
    RoleResponse,
    UserRoleResponse,
)
from app.utils.response import AppException, success_response

logger = logging.getLogger(__name__)


def _mask_sensitive_data(value: str, visible_chars: int = 3) -> str:
    """脱敏处理敏感数据。

    Args:
        value: 原始值
        visible_chars: 开头保留的字符数

    Returns:
        str: 脱敏后的值，如 "abc***def"
    """
    if not value:
        return ""
    if len(value) <= visible_chars:
        return "*" * len(value)
    return f"{value[:visible_chars]}{'*' * (len(value) - visible_chars)}"


# 创建路由器
router = APIRouter(prefix="/admin/roles")


# --------------------------------------------------------------------------
# 管理后台权限校验依赖
# --------------------------------------------------------------------------
async def require_admin(
    current_user: UserInfo = Depends(get_current_user),
) -> UserInfo:
    """管理后台权限校验：仅 admin 角色可访问。

    Args:
        current_user: 当前用户（通过认证依赖注入）

    Returns:
        UserInfo: 具有管理权限的用户信息

    Raises:
        AppException: 非管理员角色（错误码 1004）
    """
    if "admin" not in current_user.roles:
        raise AppException(1004, "无管理权限")
    return current_user


# ==========================================================================
# 1. 角色管理
# ==========================================================================

# ---------- GET /api/admin/roles ----------
@router.get("")
async def get_roles(
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """获取所有角色列表。

    返回角色列表，包含每个角色的用户数量统计。

    Args:
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式，包含角色列表
    """
    # 查询所有角色
    stmt = select(Role).order_by(Role.is_default.desc(), Role.name)
    result = await db.execute(stmt)
    roles = result.scalars().all()

    # 构建响应，包含用户数量
    role_list = []
    for role in roles:
        # 统计拥有该角色的用户数
        count_stmt = select(func.count()).select_from(UserRole).where(UserRole.role_id == role.id)
        count_result = await db.execute(count_stmt)
        user_count = count_result.scalar() or 0

        role_list.append(
            RoleResponse(
                id=role.id,
                name=role.name,
                display_name=role.display_name,
                description=role.description,
                permissions=role.permissions or [],
                is_default=role.is_default,
                user_count=user_count,
                created_at=role.created_at,
                updated_at=role.updated_at,
            )
        )

    return success_response(data=[r.model_dump() for r in role_list])


# ==========================================================================
# 2. 用户角色分配/撤销
# ==========================================================================

# ---------- POST /api/admin/roles/assign ----------
@router.post("/assign")
async def assign_role(
    body: RoleAssignRequest,
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """手动分配角色。

    为指定用户分配角色，记录分配者和分配原因。
    安全限制：禁止管理员给自己分配角色。

    Args:
        body: 分配角色请求
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式
    """
    # 安全限制：禁止管理员给自己分配角色
    if body.employee_id == admin.employee_id:
        raise AppException(4014, "不能给自己分配角色")

    # 查询目标角色
    role_stmt = select(Role).where(Role.name == body.role_name)
    role_result = await db.execute(role_stmt)
    role = role_result.scalars().first()

    if not role:
        raise AppException(4004, f"角色 {body.role_name} 不存在")

    # 检查是否已拥有该角色
    existing_stmt = select(UserRole).where(
        UserRole.employee_id == body.employee_id,
        UserRole.role_id == role.id,
    )
    existing_result = await db.execute(existing_stmt)
    existing = existing_result.scalars().first()

    if existing:
        raise AppException(4009, f"用户已拥有 {body.role_name} 角色")

    # 创建用户角色关联
    user_role = UserRole(
        employee_id=body.employee_id,
        role_id=role.id,
        source="manual",
        assigned_by=admin.employee_id,
    )
    db.add(user_role)
    await db.commit()

    logger.info(f"管理员 {_mask_sensitive_data(admin.employee_id)} 为用户 {_mask_sensitive_data(body.employee_id)} 分配角色 {body.role_name}，原因：{body.reason}")

    return success_response(message=f"角色 {body.role_name} 分配成功")


# ---------- POST /api/admin/roles/revoke ----------
@router.post("/revoke")
async def revoke_role(
    body: RoleRevokeRequest,
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """撤销角色。

    撤销指定用户的角色。
    安全限制：禁止管理员撤销自己的角色。

    Args:
        body: 撤销角色请求
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式
    """
    # 安全限制：禁止管理员撤销自己的角色
    if body.employee_id == admin.employee_id:
        raise AppException(4015, "不能撤销自己的角色")

    # 查询目标角色
    role_stmt = select(Role).where(Role.name == body.role_name)
    role_result = await db.execute(role_stmt)
    role = role_result.scalars().first()

    if not role:
        raise AppException(4004, f"角色 {body.role_name} 不存在")

    # 不允许撤销默认角色
    if role.is_default:
        raise AppException(4010, "不能撤销默认角色")

    # 查询用户角色关联
    user_role_stmt = select(UserRole).where(
        UserRole.employee_id == body.employee_id,
        UserRole.role_id == role.id,
    )
    user_role_result = await db.execute(user_role_stmt)
    user_role = user_role_result.scalars().first()

    if not user_role:
        raise AppException(4011, f"用户没有 {body.role_name} 角色")

    # 删除用户角色关联
    await db.delete(user_role)
    await db.commit()

    logger.info(f"管理员 {_mask_sensitive_data(admin.employee_id)} 撤销用户 {_mask_sensitive_data(body.employee_id)} 的角色 {body.role_name}，原因：{body.reason}")

    return success_response(message=f"角色 {body.role_name} 撤销成功")


# ==========================================================================
# 3. 角色映射规则管理
# ==========================================================================

# ---------- GET /api/admin/roles/mapping-rules ----------
@router.get("/mapping-rules")
async def get_mapping_rules(
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """获取所有角色映射规则。

    Args:
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式，包含映射规则列表
    """
    # 查询所有映射规则
    stmt = (
        select(RoleMappingRule, Role)
        .join(Role, RoleMappingRule.role_id == Role.id)
        .order_by(RoleMappingRule.priority.desc(), RoleMappingRule.source_type)
    )
    result = await db.execute(stmt)
    rules = result.all()

    # 构建响应
    rule_list = []
    for rule, role in rules:
        rule_list.append(
            RoleMappingRuleResponse(
                id=rule.id,
                role_id=rule.role_id,
                role_name=role.name,
                source_type=rule.source_type,
                source_value=rule.source_value,
                priority=rule.priority,
                is_active=rule.is_active,
                created_at=rule.created_at,
            )
        )

    return success_response(data=[r.model_dump() for r in rule_list])


# ---------- POST /api/admin/roles/mapping-rules ----------
@router.post("/mapping-rules")
async def create_mapping_rule(
    body: RoleMappingRuleRequest,
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """创建角色映射规则。

    Args:
        body: 创建映射规则请求
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式，包含新创建的规则 ID
    """
    # 查询目标角色
    role_stmt = select(Role).where(Role.name == body.role_name)
    role_result = await db.execute(role_stmt)
    role = role_result.scalars().first()

    if not role:
        raise AppException(4004, f"角色 {body.role_name} 不存在")

    # 检查是否已存在相同的规则
    existing_stmt = select(RoleMappingRule).where(
        RoleMappingRule.role_id == role.id,
        RoleMappingRule.source_type == body.source_type,
        RoleMappingRule.source_value == body.source_value,
    )
    existing_result = await db.execute(existing_stmt)
    existing = existing_result.scalars().first()

    if existing:
        raise AppException(4012, "已存在相同的映射规则")

    # 创建映射规则
    rule = RoleMappingRule(
        role_id=role.id,
        source_type=body.source_type,
        source_value=body.source_value,
        priority=body.priority,
        is_active=body.is_active,
    )
    db.add(rule)
    await db.commit()

    logger.info(f"管理员 {_mask_sensitive_data(admin.employee_id)} 创建映射规则：{body.source_type}={body.source_value} → {body.role_name}")

    return success_response(
        message="映射规则创建成功",
        data={"id": rule.id},
    )


# ---------- DELETE /api/admin/roles/mapping-rules/{rule_id} ----------
@router.delete("/mapping-rules/{rule_id}")
async def delete_mapping_rule(
    rule_id: str,
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """删除角色映射规则。

    Args:
        rule_id: 规则 ID
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式
    """
    # 查询规则
    rule_stmt = select(RoleMappingRule).where(RoleMappingRule.id == rule_id)
    rule_result = await db.execute(rule_stmt)
    rule = rule_result.scalars().first()

    if not rule:
        raise AppException(4013, "映射规则不存在")

    # 删除规则
    await db.delete(rule)
    await db.commit()

    logger.info(f"管理员 {_mask_sensitive_data(admin.employee_id)} 删除映射规则 {rule_id}")

    return success_response(message="映射规则删除成功")


# ==========================================================================
# 4. 权限矩阵可视化 (v0.7.1 task #86)
# ==========================================================================
# 给管理后台 UI 用: 返回 5 角色 × 4 资源 × 4 操作 × 3 范围的完整矩阵
# 嵌套结构方便前端直接渲染表格:
#   {
#     "roles": [{name, display_name, permissions: [string]}],
#     "resources": [conversation, agent, ...],
#     "actions": [read, create, update, delete],
#     "scopes": [own, department, all],
#     "matrix": {
#       "agent": {  # 角色名
#         "conversation:read:own": true,
#         "conversation:read:all": true,
#         ...
#       }
#     }
#   }
# ==========================================================================
@router.get("/permissions/matrix")
async def get_permissions_matrix(
    admin: UserInfo = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """获取 RBAC 完整权限矩阵(管理后台可视化用)。

    返回 5 角色预置的 permissions JSON,前端用此数据渲染
    角色 × 资源 × 操作 × 范围 的可读表格。

    Args:
        admin: 管理员（权限校验）
        db: 数据库会话

    Returns:
        Dict: 统一响应格式,包含完整权限矩阵
    """
    from app.services.rbac_service import (
        ROLE_PERMISSIONS,
        VALID_ACTIONS,
        VALID_RESOURCES,
        VALID_SCOPES,
        permissions_to_strings,
    )

    # 1. 查 DB 拿角色元数据(显示名等)
    stmt = select(Role).order_by(Role.is_default.desc(), Role.name)
    result = await db.execute(stmt)
    roles = result.scalars().all()

    # 2. 构建角色列表(以代码里的 ROLE_PERMISSIONS 为准,DB 字段作 display_name)
    role_list = []
    matrix = {}
    for role in roles:
        # 优先用代码常量(单一可信源);DB 字段仅作元数据
        perms = ROLE_PERMISSIONS.get(role.name, set())
        perms_list = permissions_to_strings(perms)

        role_list.append({
            "name": role.name,
            "display_name": role.display_name,
            "description": role.description,
            "is_default": role.is_default,
            "permission_count": len(perms_list),
        })

        # 3. 角色 × 资源 × 操作 × 范围 的全矩阵
        #    true/false 表征是否拥有此权限
        #    前端用此渲染表格,空格表示"不适用"
        role_matrix = {}
        for resource in VALID_RESOURCES:
            for action in VALID_ACTIONS:
                for scope in VALID_SCOPES:
                    perm = f"{resource}:{action}:{scope}"
                    role_matrix[perm] = (resource, action, scope) in perms
        matrix[role.name] = role_matrix

    return success_response(data={
        "roles": role_list,
        "resources": VALID_RESOURCES,
        "actions": VALID_ACTIONS,
        "scopes": VALID_SCOPES,
        "matrix": matrix,
    })


# ---------- GET /api/admin/roles/permissions/check ----------
# 给前端按钮级权限控制用: 传入 (resource, action, scope) 查当前用户是否拥有
# 注: 这是 endpoint 版本,装饰器版本见 app.dependencies.require_permission
@router.get("/permissions/check")
async def check_my_permission(
    resource: str = Query(..., description="资源"),
    action: str = Query(..., description="操作"),
    scope: str = Query("own", description="数据范围"),
    admin: UserInfo = Depends(require_admin),
):
    """检查当前管理员是否拥有指定权限(给前端按钮级控制用)。

    永远返回 true(因为 require_admin 已确保是 admin)。
    此端点存在是为了给前端一个统一入口,实际权限由后端强制。
    未来扩展:可加 current_user 参数(非 admin 角色也能调)。

    Args:
        resource: 资源
        action: 操作
        scope: 数据范围

    Returns:
        Dict: 统一响应格式,包含 has_permission 字段
    """
    from app.services.rbac_service import check_permission, ROLE_PERMISSIONS, permissions_to_strings

    user_perms = {role: permissions_to_strings(perms) for role, perms in ROLE_PERMISSIONS.items()}

    has_perm = check_permission(
        user_roles=admin.roles,
        user_permissions=user_perms,
        required_resource=resource,
        required_action=action,
        required_scope=scope,
    )

    return success_response(data={
        "has_permission": has_perm,
        "resource": resource,
        "action": action,
        "scope": scope,
    })
