"""
Rutas API para el sistema de permisos multi-tenant.
Endpoints para gestión de permisos, roles y asignaciones.
"""

from typing import List, Optional, Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session

from core.database import get_core_db
from core.security import (
    collect_user_role_names,
    get_current_user,
    validate_access_to_resource,
    is_hub_admin,
)
from .dependencies import (
    PermissionChecker,
    RequirePermission,
    get_permission_service,
    get_current_user_permissions,
)
from .cache import PermissionCache
from .service import PermissionService
from .models import Permission, CompanyRole
from .schemas import (
    PermissionResponse,
    CompanyRoleResponse,
    UserPermissionsResponse,
    AssignRoleRequest,
    GrantPermissionRequest,
    SuccessResponse,
    CreateRoleRequest,
    UpdateRoleRequest,
    AssignPermissionsToRoleRequest,
    PermissionListResponse,
    RoleListResponse,
    UserRoleListResponse,
    AssignUserRoleRequest,
    UserCompanyRoleResponse,
    UserPermissionResponse,
    AssignUserPermissionRequest,
    UserPermissionsListResponse,
    EffectiveUserPermissionsResponse,
)


router = APIRouter(prefix="/permissions", tags=["permissions"])


def _tenant_if_roles_or_user_admin(
    db: Session,
    company_id: int,
    current_user: Dict[str, Any],
) -> int:
    """
    Acceso a plantillas de rol y asignaciones en compañía: ``roles.view`` o gestión de
    usuarios (``user.view`` / ``user.update`` / ``user.manage``). Evita exigir
    ``roles.view`` a quien solo administra usuarios con permisos de app.
    """
    return validate_access_to_resource(
        db,
        company_id,
        current_user,
        ["roles.view", "user.view", "user.update", "user.manage"],
        require_all=False,
    )


# RUTAS DE CONSULTA DE PERMISOS


@router.get("/me", response_model=UserPermissionsResponse)
async def get_my_permissions(
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    permission_service: PermissionService = Depends(get_permission_service),
):
    """
    Obtiene los permisos y roles del usuario actual en la compañía actual.
    Si la compañía no tiene roles definidos, realiza un bootstrap automático.
    También realiza bootstrap si es un usuario con rol 'admin' en Keycloak pero sin roles locales.
    """
    # 1. Validar acceso básico a la compañía
    # Nota: pass None en required_permissions permite el paso al bootstrap
    tenant_id = validate_access_to_resource(db, company_id, current_user)
    
    user_id = current_user.get("sub") or current_user.get("id")

    # 2. Determinar si es un admin de Keycloak para forzar bootstrap si es necesario
    # Admin global: rol "admin" en Keycloak o hub_admin del Hub.
    is_keycloak_admin = "admin" in collect_user_role_names(current_user) or is_hub_admin(current_user)

    # 3. Bootstrap: si la compañía no tiene roles, o si el usuario es admin, o si estamos en desarrollo y el usuario no tiene roles
    from .models import CompanyRole, UserCompanyRole
    from core.config import settings
    
    has_roles = db.query(CompanyRole).filter(CompanyRole.company_id == company_id).first() is not None
    user_has_roles = db.query(UserCompanyRole).filter(
        UserCompanyRole.user_id == user_id, 
        UserCompanyRole.company_id == company_id
    ).first() is not None

    auto_bootstrap = (not has_roles) or \
                     (is_keycloak_admin and not user_has_roles) or \
                     (settings.ENVIRONMENT == "development" and (not user_has_roles or is_keycloak_admin))

    if auto_bootstrap:
        # Nota: bootstrap_super_admin ya hace commit e intenta no duplicar si el rol ya existe
        permission_service.bootstrap_super_admin(user_id, company_id)
    
    # 4. Obtener permisos finales (caché habilitado en PermissionService)
    permissions = permission_service.get_user_permissions(
        user_id, company_id, use_cache=True
    )

    # 5. Obtener roles locales. Devolvemos el `code` (estable, kebab/snake)
    # porque el frontend lo usa para checks (p.ej. bypass de "super_admin").
    # Los `name` legibles se exponen en otros endpoints de gestión de roles.
    roles = permission_service.get_user_roles(user_id, company_id)
    role_codes = [role.code for role in roles]

    # 6. Derivar sistemas permitidos desde los permisos de sistema
    allowed_systems = [
        sys for sys in ("inventory", "fixed_asset")
        if f"system.{sys}.access" in permissions
    ]

    return UserPermissionsResponse(
        user_id=user_id,
        company_id=company_id,
        tenant_id=tenant_id,
        permissions=list(permissions),
        roles=role_codes,
        allowed_systems=allowed_systems,
    )


@router.get("/available", response_model=PermissionListResponse)
async def list_available_permissions(
    db: Session = Depends(get_core_db),
    page: int = Query(1, ge=1, description="Número de página"),
    page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
    module: Optional[str] = Query(None, description="Filtrar por módulo"),
    _: None = Depends(RequirePermission("roles.view")),
):
    """
    Lista todos los permisos disponibles en el sistema.
    Requiere permiso: permissions.view
    """
    query = db.query(Permission).filter(Permission.is_active == True)

    if module:
        query = query.filter(Permission.module == module)

    total = query.count()
    permissions = query.offset((page - 1) * page_size).limit(page_size).all()

    return PermissionListResponse(
        items=permissions, total=total, page=page, page_size=page_size
    )


@router.get("/roles", response_model=RoleListResponse)
async def list_company_roles(
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    page: int = Query(1, ge=1, description="Número de página"),
    page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
):
    """
    Lista todos los roles del companye actual.
    TODO: Agregar verificación de permisos
    """
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)

    query = db.query(CompanyRole).filter(
        CompanyRole.company_id == company_id
    )

    total = query.count()
    roles = query.offset((page - 1) * page_size).limit(page_size).all()

    return RoleListResponse(items=roles, total=total, page=page, page_size=page_size)


@router.get("/users/{user_id}", response_model=UserPermissionsResponse)
async def get_user_permissions(
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    permission_service: PermissionService = Depends(get_permission_service),
):
    """
    Obtiene los permisos y roles de un usuario específico.
    Requiere roles.view o permisos de gestión de usuarios en la compañía.
    """
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)

    permissions = permission_service.get_user_permissions(
        user_id, company_id, use_cache=True
    )
    roles = permission_service.get_user_roles(user_id, company_id)
    role_codes = [role.code for role in roles]

    return UserPermissionsResponse(
        user_id=user_id,
        company_id=company_id,
        tenant_id=tenant_id,
        permissions=list(permissions),
        roles=role_codes,
    )


# RUTAS CRUD DE PERMISOS


@router.get("", response_model=PermissionListResponse)
async def list_permissions(
    db: Session = Depends(get_core_db),
    page: int = Query(1, ge=1, description="Número de página"),
    page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
    module: Optional[str] = Query(None, description="Filtrar por módulo"),
    action: Optional[str] = Query(None, description="Filtrar por acción"),
    search: Optional[str] = Query(None, description="Buscar por código o descripción"),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Lista todos los permisos disponibles en el sistema.
    TODO: Agregar verificación de permisos
    """
    query = db.query(Permission).filter(Permission.is_active == True)

    if module:
        query = query.filter(Permission.module == module)
    if action:
        query = query.filter(Permission.action == action)
    if search:
        search_filter = f"%{search}%"
        query = query.filter(
            (Permission.code.ilike(search_filter))
            | (Permission.description.ilike(search_filter))
        )

    total = query.count()
    permissions = query.offset((page - 1) * page_size).limit(page_size).all()

    return PermissionListResponse(
        items=permissions, total=total, page=page, page_size=page_size
    )


@router.get("/modules", response_model=List[str])
async def get_modules(
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Obtiene la lista de módulos únicos.
    TODO: Agregar verificación de permisos
    """
    modules = (
        db.query(Permission.module)
        .filter(Permission.is_active == True)
        .distinct()
        .all()
    )
    return [m[0] for m in modules]


@router.get("/actions", response_model=List[str])
async def get_actions(
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Obtiene la lista de acciones únicas.
    TODO: Agregar verificación de permisos
    """
    actions = (
        db.query(Permission.action)
        .filter(Permission.is_active == True)
        .distinct()
        .all()
    )
    return [a[0] for a in actions]


# RUTAS DE GESTIÓN DE ASIGNACIONES DE ROLES A USUARIOS


@router.get("/user-roles", response_model=UserRoleListResponse)
async def list_user_roles(
    company_id: int = Query(..., description="Company ID"),
    user_id: Optional[str] = Query(None, description="Filtrar por user_id"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    page: int = Query(1, ge=1, description="Número de página"),
    page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
):
    """
    Lista todas las asignaciones de roles a usuarios en la compañía.
    """
    from .models import UserCompanyRole
    from sqlalchemy.orm import joinedload
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    query = db.query(UserCompanyRole).options(
        joinedload(UserCompanyRole.company_role)
    ).filter(
        UserCompanyRole.company_id == company_id,
        UserCompanyRole.tenant_id == tenant_id
    )
    
    if user_id:
        query = query.filter(UserCompanyRole.user_id == user_id)
    
    total = query.count()
    user_roles = query.offset((page - 1) * page_size).limit(page_size).all()
    
    return UserRoleListResponse(
        items=user_roles,
        total=total,
        page=page,
        page_size=page_size
    )


@router.post("/user-roles", response_model=UserCompanyRoleResponse, status_code=status.HTTP_201_CREATED)
async def assign_user_role(
    request: AssignUserRoleRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Asigna un rol a un usuario.
    """
    from .models import UserCompanyRole, CompanyRole
    from sqlalchemy.exc import IntegrityError
    from sqlalchemy.orm import joinedload
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    assigner_id = current_user.get("sub") or current_user.get("id")
    
    # Verificar que el rol existe
    role = db.query(CompanyRole).filter(
        CompanyRole.id == request.company_role_id,
        CompanyRole.company_id == company_id
    ).first()
    
    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Role not found"
        )
    
    # Crear la asignación
    user_role = UserCompanyRole(
        user_id=request.user_id,
        company_id=company_id,
        tenant_id=tenant_id,
        company_role_id=request.company_role_id,
        assigned_by=assigner_id,
        is_active=True
    )
    
    try:
        db.add(user_role)
        db.commit()
        db.refresh(user_role)

        # Invalidar caché de permisos del usuario afectado
        try:
            PermissionCache().invalidate_user(tenant_id, company_id, request.user_id)
        except Exception:
            pass

        # Recargar con la relación company_role
        user_role = db.query(UserCompanyRole).options(
            joinedload(UserCompanyRole.company_role)
        ).filter(UserCompanyRole.id == user_role.id).first()
        
        return user_role
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="User already has this role assigned"
        )


@router.delete("/user-roles/{user_role_id}")
async def remove_user_role(
    user_role_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Remueve una asignación de rol a usuario.
    """
    from .models import UserCompanyRole
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    user_role = db.query(UserCompanyRole).filter(
        UserCompanyRole.id == user_role_id,
        UserCompanyRole.company_id == company_id,
        UserCompanyRole.tenant_id == tenant_id
    ).first()
    
    if not user_role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User role assignment not found"
        )
    
    db.delete(user_role)
    db.commit()

    # Invalidar caché de permisos del usuario afectado
    try:
        PermissionCache().invalidate_user(tenant_id, company_id, user_role.user_id)
    except Exception:
        pass

    return {"success": True, "message": "User role assignment removed"}


@router.get("/{permission_id}", response_model=PermissionResponse)
async def get_permission(
    permission_id: int,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Obtiene un permiso por ID.
    TODO: Agregar verificación de permisos
    """
    permission = db.query(Permission).filter(Permission.id == permission_id).first()
    if not permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Permission not found"
        )
    return permission



@router.post("/sync", response_model=Dict[str, Any])
async def sync_permissions_endpoint(
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    permission_service: PermissionService = Depends(get_permission_service),
    _: None = Depends(RequirePermission("roles.edit")),
):
    """
    Sincroniza dinámicamente los permisos registrados en los módulos con la base de datos.
    Requiere permiso: permissions.edit
    """
    result = permission_service.sync_permissions()
    return {
        "success": True,
        "message": "Permissions synchronized successfully",
        "data": result
    }


from .schemas import CreatePermissionRequest


@router.post(
    "", response_model=PermissionResponse, status_code=status.HTTP_201_CREATED
)
async def create_permission(
    request: CreatePermissionRequest,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Crea un nuevo permiso en el sistema.
    TODO: Agregar verificación de permisos
    """
    # Verificar que el código no esté en uso
    existing = db.query(Permission).filter(Permission.code == request.code).first()
    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Permission with code '{request.code}' already exists",
        )

    permission = Permission(
        code=request.code,
        description=request.description,
        module=request.module,
        action=request.action,
    )
    db.add(permission)
    db.commit()
    db.refresh(permission)
    return permission


@router.put("/{permission_id}", response_model=PermissionResponse)
async def update_permission(
    permission_id: int,
    request: CreatePermissionRequest,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Actualiza un permiso existente.
    TODO: Agregar verificación de permisos
    """
    permission = db.query(Permission).filter(Permission.id == permission_id).first()
    if not permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Permission not found"
        )

    # Si se cambia el código, verificar que no exista
    if request.code != permission.code:
        existing = db.query(Permission).filter(Permission.code == request.code).first()
        if existing:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Permission with code '{request.code}' already exists",
            )

    permission.code = request.code
    permission.description = request.description
    permission.module = request.module
    permission.action = request.action
    db.commit()
    db.refresh(permission)
    return permission


@router.delete("/{permission_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_permission(
    permission_id: int,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Elimina (desactiva) un permiso.
    TODO: Agregar verificación de permisos
    """
    permission = db.query(Permission).filter(Permission.id == permission_id).first()
    if not permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Permission not found"
        )

    permission.is_active = False
    db.commit()


# RUTAS DE GESTIÓN DE ROLES


@router.post(
    "/roles", response_model=CompanyRoleResponse, status_code=status.HTTP_201_CREATED
)
async def create_role(
    request: CreateRoleRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Crea un nuevo rol personalizado para el companye.
    TODO: Agregar verificación de permisos
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.create"])
    
    # Verificar que el código no esté en uso
    existing = (
        db.query(CompanyRole)
        .filter(CompanyRole.company_id == company_id, CompanyRole.code == request.code)
        .first()
    )

    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Role with code '{request.code}' already exists for this company",
        )

    # Crear el rol
    role = CompanyRole(
        company_id=company_id,
        tenant_id=tenant_id,
        name=request.name,
        code=request.code,
        description=request.description,
    )

    db.add(role)
    db.commit()
    db.refresh(role)

    # Asignar permisos si se especificaron
    if request.permission_ids:
        from .models import RolePermission

        for perm_id in request.permission_ids:
            role_perm = RolePermission(company_role_id=role.id, permission_id=perm_id)
            db.add(role_perm)
        db.commit()

    return role


@router.patch("/roles/{role_id}", response_model=CompanyRoleResponse)
async def update_role(
    role_id: int,
    request: UpdateRoleRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Actualiza un rol existente.
    TODO: Agregar verificación de permisos
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.edit"])
    
    role = (
        db.query(CompanyRole)
        .filter(CompanyRole.id == role_id, CompanyRole.company_id == company_id)
        .first()
    )

    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
        )

    # Actualizar campos
    if request.name is not None:
        role.name = request.name
    if request.description is not None:
        role.description = request.description
    if request.is_active is not None:
        role.is_active = request.is_active

    db.commit()
    db.refresh(role)

    return role


@router.delete("/roles/{role_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_role(
    role_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Elimina permanentemente un rol y sus permisos asociados.
    TODO: Agregar verificación de permisos
    """
    from .models import RolePermission, UserCompanyRole
    
    tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.delete"])
    
    role = (
        db.query(CompanyRole)
        .filter(CompanyRole.id == role_id, CompanyRole.company_id == company_id)
        .first()
    )

    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
        )

    # Verificar si hay usuarios con este rol
    users_with_role = db.query(UserCompanyRole).filter(
        UserCompanyRole.company_role_id == role_id
    ).count()
    
    if users_with_role > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"No se puede eliminar el rol porque tiene {users_with_role} usuario(s) asignado(s). Primero remueve el rol de los usuarios."
        )

    # Eliminar permisos asociados al rol
    db.query(RolePermission).filter(RolePermission.company_role_id == role_id).delete()
    
    # Eliminar el rol
    db.delete(role)
    db.commit()

    # Cambiar la composición de permisos de rol invalida el caché de la compañía
    try:
        PermissionCache().invalidate_company(company_id)
    except Exception:
        pass


# RUTAS DE GESTIÓN DE PERMISOS POR ROL


@router.get("/roles/{role_id}/permissions")
async def get_role_permissions(
    role_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Obtiene todos los permisos asignados a un rol.
    """
    from .models import RolePermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # Verificar que el rol existe y pertenece al company
    role = (
        db.query(CompanyRole)
        .filter(CompanyRole.id == role_id, CompanyRole.company_id == company_id)
        .first()
    )
    
    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
        )
    
    # Obtener permisos del rol con información completa
    role_permissions = (
        db.query(RolePermission)
        .join(Permission, RolePermission.permission_id == Permission.id)
        .filter(RolePermission.company_role_id == role_id)
        .all()
    )
    
    permissions_list = []
    for rp in role_permissions:
        permission = db.query(Permission).filter(Permission.id == rp.permission_id).first()
        permissions_list.append({
            "id": rp.id,
            "company_role_id": rp.company_role_id,
            "permission_id": rp.permission_id,
            "permission": {
                "id": permission.id,
                "code": permission.code,
                "module": permission.module,
                "action": permission.action,
                "description": permission.description
            } if permission else None
        })
    
    return {
        "role_id": role_id,
        "permissions": permissions_list,
        "total": len(permissions_list)
    }


@router.post("/roles/{role_id}/permissions")
async def assign_permission_to_role(
    role_id: int,
    permission_id: int = Query(..., description="Permission ID"),
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Asigna un permiso a un rol.
    """
    from .models import RolePermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # Verificar que el rol existe
    role = (
        db.query(CompanyRole)
        .filter(CompanyRole.id == role_id, CompanyRole.company_id == company_id)
        .first()
    )
    
    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
        )
    
    # Verificar que el permiso existe
    permission = db.query(Permission).filter(Permission.id == permission_id).first()
    
    if not permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Permission not found"
        )
    
    # Verificar si ya existe
    existing = (
        db.query(RolePermission)
        .filter(
            RolePermission.company_role_id == role_id,
            RolePermission.permission_id == permission_id
        )
        .first()
    )
    
    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Permission already assigned to this role"
        )
    
    # Crear la asignación
    role_permission = RolePermission(
        company_role_id=role_id,
        permission_id=permission_id,
        tenant_id=tenant_id,
        company_id=company_id
    )
    
    db.add(role_permission)
    db.commit()
    db.refresh(role_permission)

    # Cualquier cambio en permisos por rol invalida el caché de la compañía
    try:
        PermissionCache().invalidate_company(company_id)
    except Exception:
        pass

    return {"success": True, "message": "Permission assigned to role"}


@router.post("/roles/{role_id}/permissions/batch")
async def assign_multiple_permissions_to_role(
    role_id: int,
    request: AssignPermissionsToRoleRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Asigna múltiples permisos a un rol.
    """
    from .models import RolePermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # Verificar que el rol existe
    role = (
        db.query(CompanyRole)
        .filter(CompanyRole.id == role_id, CompanyRole.company_id == company_id)
        .first()
    )
    
    if not role:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
        )
    
    added_count = 0
    
    for permission_id in request.permission_ids:
        # Verificar que el permiso existe
        permission = db.query(Permission).filter(Permission.id == permission_id).first()
        
        if not permission:
            continue  # Saltear permisos que no existen
        
        # Verificar si ya existe
        existing = (
            db.query(RolePermission)
            .filter(
                RolePermission.company_role_id == role_id,
                RolePermission.permission_id == permission_id
            )
            .first()
        )
        
        if existing:
            continue  # Saltear si ya existe
        
        # Crear la asignación
        role_permission = RolePermission(
            company_role_id=role_id,
            permission_id=permission_id,
            tenant_id=tenant_id,
            company_id=company_id
        )
        
        db.add(role_permission)
        added_count += 1
    
    db.commit()

    # Invalida caché para todos los usuarios de la compañía
    try:
        PermissionCache().invalidate_company(company_id)
    except Exception:
        pass

    return {
        "success": True,
        "message": f"{added_count} permissions assigned to role",
        "added_count": added_count
    }


@router.delete("/roles/{role_id}/permissions/{permission_id}")
async def remove_permission_from_role(
    role_id: int,
    permission_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Remueve un permiso de un rol.
    """
    from .models import RolePermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # Buscar la asignación
    role_permission = (
        db.query(RolePermission)
        .filter(
            RolePermission.company_role_id == role_id,
            RolePermission.permission_id == permission_id
        )
        .first()
    )
    
    if not role_permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Permission assignment not found"
        )
    
    db.delete(role_permission)
    db.commit()

    try:
        PermissionCache().invalidate_company(company_id)
    except Exception:
        pass

    return {"success": True, "message": "Permission removed from role"}


# RUTAS DE ASIGNACIÓN DE ROLES Y PERMISOS


@router.post(
    "/assign-role", response_model=SuccessResponse, status_code=status.HTTP_201_CREATED
)
async def assign_role(
    request: AssignRoleRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    permission_service: PermissionService = Depends(get_permission_service),
):
    """
    Asigna un rol a un usuario en el companye actual.
    TODO: Agregar verificación de permisos
    """
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    assigner_id = current_user.get("sub") or current_user.get("id")

    try:
        user_role = permission_service.assign_role_to_user(
            user_id=request.user_id,
            company_id=company_id,
            role_id=request.role_id,
            assigned_by=assigner_id,
        )

        return SuccessResponse(
            message=f"Role assigned to user {request.user_id}",
            data={"assignment_id": user_role.id},
        )
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))


@router.post(
    "/grant-permission",
    response_model=SuccessResponse,
    status_code=status.HTTP_201_CREATED,
)
async def grant_permission(
    request: GrantPermissionRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
    permission_service: PermissionService = Depends(get_permission_service),
):
    """
    Concede un permiso directo a un usuario en el companye actual.
    Requiere ``permissions.grant`` o gestión de usuarios (``user.manage`` / ``user.update``).
    """
    tenant_id = validate_access_to_resource(
        db,
        company_id,
        current_user,
        ["permissions.grant", "user.manage", "user.update"],
        require_all=False,
    )
    
    assigner_id = current_user.get("sub") or current_user.get("id")

    try:
        user_permission = permission_service.grant_direct_permission(
            user_id=request.user_id,
            company_id=company_id,
            permission_code=request.permission_code,
            assigned_by=assigner_id,
            expires_at=request.expires_at,
        )

        return SuccessResponse(
            message=f"Permission {request.permission_code} granted to user {request.user_id}",
            data={
                "permission_id": user_permission.id,
                "expires_at": (
                    user_permission.expires_at.isoformat()
                    if user_permission.expires_at
                    else None
                ),
            },
        )
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))


# EJEMPLOS DE RUTAS PROTEGIDAS CON PERMISOS


@router.get("/examples/invoices")
async def example_view_invoices(_: None = Depends(RequirePermission("invoice.view"))):
    """
    Endpoint de ejemplo que requiere el permiso invoice.view
    """
    return {
        "message": "You have permission to view invoices",
        "invoices": [
            {"id": 1, "number": "INV-001", "amount": 1000},
            {"id": 2, "number": "INV-002", "amount": 2000},
        ],
    }


@router.post("/examples/invoices")
async def example_edit_invoice(
    _: None = Depends(
        PermissionChecker(["invoice.view", "invoice.edit"], require_all=True)
    )
):
    """
    Endpoint de ejemplo que requiere AMBOS permisos: invoice.view e invoice.edit
    """
    return {
        "message": "Invoice updated successfully",
        "invoice": {"id": 1, "number": "INV-001", "amount": 1500},
    }


@router.get("/examples/reports")
async def example_view_reports(
    _: None = Depends(
        PermissionChecker(
            ["report.financial.view", "report.admin.view"], require_all=False
        )
    )
):
    """
    Endpoint de ejemplo que requiere AL MENOS UNO de los permisos especificados.
    """
    return {
        "message": "Financial reports",
        "reports": ["Monthly P&L", "Cash Flow", "Balance Sheet"],
    }


@router.get("/examples/dashboard")
async def example_dashboard(
    user_permissions: set = Depends(get_current_user_permissions),
):
    """
    Dashboard dinámico que muestra diferentes widgets según los permisos del usuario.
    """
    widgets = []

    if "invoice.view" in user_permissions:
        widgets.append(
            {
                "type": "invoices",
                "title": "Recent Invoices",
                "data": [{"id": 1, "number": "INV-001"}],
            }
        )

    if "report.financial.view" in user_permissions:
        widgets.append(
            {
                "type": "financial",
                "title": "Financial Summary",
                "data": {"revenue": 50000, "expenses": 30000},
            }
        )

    if "user.view" in user_permissions:
        widgets.append(
            {"type": "users", "title": "User Activity", "data": {"active_users": 42}}
        )

    return {"permissions": list(user_permissions), "widgets": widgets}


# RUTAS DE GESTIÓN DE PERMISOS INDIVIDUALES DE USUARIO


@router.get("/users/{user_id}/permissions")
async def get_user_individual_permissions(
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
) -> UserPermissionsListResponse:
    """
    Obtiene los permisos individuales asignados a un usuario específico
    (no incluye los permisos heredados de roles).
    """
    from .models import UserCompanyPermission
    from sqlalchemy.orm import joinedload
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    user_permissions = (
        db.query(UserCompanyPermission)
        .options(joinedload(UserCompanyPermission.permission))
        .filter(
            UserCompanyPermission.user_id == user_id,
            UserCompanyPermission.company_id == company_id,
            UserCompanyPermission.tenant_id == tenant_id,
            UserCompanyPermission.is_active == True,
        )
        .all()
    )
    
    return UserPermissionsListResponse(
        items=[UserPermissionResponse.model_validate(up) for up in user_permissions],
        total=len(user_permissions),
    )


@router.get("/users/{user_id}/permissions/effective")
async def get_user_effective_permissions(
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
) -> EffectiveUserPermissionsResponse:
    """
    Obtiene los permisos efectivos de un usuario:
    - Permisos de roles
    - Permisos individuales concedidos
    - Permisos revocados
    - Permisos finales (roles + concedidos - revocados)
    """
    from .models import UserCompanyPermission, UserCompanyRole, RolePermission
    from sqlalchemy.orm import joinedload
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # 1. Obtener permisos de roles
    role_permissions_query = (
        db.query(Permission)
        .join(RolePermission, RolePermission.permission_id == Permission.id)
        .join(CompanyRole, CompanyRole.id == RolePermission.company_role_id)
        .join(UserCompanyRole, UserCompanyRole.company_role_id == CompanyRole.id)
        .filter(
            UserCompanyRole.user_id == user_id,
            UserCompanyRole.company_id == company_id,
            UserCompanyRole.tenant_id == tenant_id,
            UserCompanyRole.is_active == True,
            CompanyRole.is_active == True,
            Permission.is_active == True,
        )
        .distinct()
        .all()
    )
    
    # 2. Obtener permisos individuales
    individual_permissions = (
        db.query(UserCompanyPermission)
        .options(joinedload(UserCompanyPermission.permission))
        .filter(
            UserCompanyPermission.user_id == user_id,
            UserCompanyPermission.company_id == company_id,
            UserCompanyPermission.tenant_id == tenant_id,
            UserCompanyPermission.is_active == True,
        )
        .all()
    )
    
    granted_permissions = [
        up.permission for up in individual_permissions 
        if up.is_granted and up.permission and up.permission.is_active
    ]
    
    revoked_permissions = [
        up.permission for up in individual_permissions 
        if not up.is_granted and up.permission and up.permission.is_active
    ]
    
    # 3. Calcular permisos efectivos
    revoked_ids = {p.id for p in revoked_permissions}
    role_perms_filtered = [p for p in role_permissions_query if p.id not in revoked_ids]
    
    # Combinar y eliminar duplicados
    all_perms = {}
    for p in role_perms_filtered:
        all_perms[p.id] = p
    for p in granted_permissions:
        all_perms[p.id] = p
    
    effective_permissions = list(all_perms.values())
    
    return EffectiveUserPermissionsResponse(
        user_id=user_id,
        company_id=company_id,
        role_permissions=[PermissionResponse.model_validate(p) for p in role_permissions_query],
        granted_permissions=[PermissionResponse.model_validate(p) for p in granted_permissions],
        revoked_permissions=[PermissionResponse.model_validate(p) for p in revoked_permissions],
        effective_permissions=[PermissionResponse.model_validate(p) for p in effective_permissions],
    )


@router.post("/users/{user_id}/permissions", status_code=status.HTTP_201_CREATED)
async def assign_user_permission(
    user_id: str,
    request: AssignUserPermissionRequest,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
) -> UserPermissionResponse:
    """
    Asigna un permiso individual a un usuario.
    - is_granted=True: Concede el permiso (útil para permisos extra)
    - is_granted=False: Revoca el permiso (útil para quitar permisos del rol)
    """
    from .models import UserCompanyPermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    # Verificar que el permiso existe
    permission = db.query(Permission).filter(Permission.id == request.permission_id).first()
    if not permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Permission not found"
        )
    
    # Verificar si ya existe
    existing = (
        db.query(UserCompanyPermission)
        .filter(
            UserCompanyPermission.user_id == user_id,
            UserCompanyPermission.company_id == company_id,
            UserCompanyPermission.tenant_id == tenant_id,
            UserCompanyPermission.permission_id == request.permission_id,
        )
        .first()
    )
    
    if existing:
        # Actualizar existente
        existing.is_granted = request.is_granted
        existing.is_active = True
        existing.assigned_by = current_user.get("sub")
        existing.expires_at = request.expires_at
        db.commit()
        db.refresh(existing)
        # Invalidar caché del usuario afectado
        try:
            PermissionCache().invalidate_user(tenant_id, company_id, user_id)
        except Exception:
            pass
        return UserPermissionResponse.model_validate(existing)
    
    # Crear nuevo
    user_permission = UserCompanyPermission(
        user_id=user_id,
        company_id=company_id,
        tenant_id=tenant_id,
        permission_id=request.permission_id,
        is_granted=request.is_granted,
        assigned_by=current_user.get("sub"),
        expires_at=request.expires_at,
    )
    
    db.add(user_permission)
    db.commit()
    db.refresh(user_permission)

    # Invalidar caché del usuario afectado
    try:
        PermissionCache().invalidate_user(tenant_id, company_id, user_id)
    except Exception:
        pass

    return UserPermissionResponse.model_validate(user_permission)


@router.delete("/users/{user_id}/permissions/{permission_id}", status_code=status.HTTP_204_NO_CONTENT)
async def remove_user_permission(
    user_id: str,
    permission_id: int,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user),
):
    """
    Elimina un permiso individual de un usuario.
    """
    from .models import UserCompanyPermission
    
    tenant_id = _tenant_if_roles_or_user_admin(db, company_id, current_user)
    
    user_permission = (
        db.query(UserCompanyPermission)
        .filter(
            UserCompanyPermission.user_id == user_id,
            UserCompanyPermission.company_id == company_id,
            UserCompanyPermission.tenant_id == tenant_id,
            UserCompanyPermission.permission_id == permission_id,
        )
        .first()
    )
    
    if not user_permission:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User permission not found"
        )
    
    db.delete(user_permission)
    db.commit()

    # Invalidar caché del usuario afectado
    try:
        PermissionCache().invalidate_user(tenant_id, company_id, user_id)
    except Exception:
        pass
