from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List

from database import get_db
from models.role import Role, Permission
from models.user import User
from schemas.role import (
    RoleResponse, RoleCreate, RoleUpdate, PermissionResponse
)
from utils.auth import require_admin_permission

router = APIRouter()


def _role_to_response(role: Role) -> RoleResponse:
    return RoleResponse(
        id=role.id,
        name=role.name,
        description=role.description,
        is_system=role.is_system,
        permissions=[PermissionResponse.model_validate(p) for p in role.permissions],
        user_count=len(role.users),
        created_at=role.created_at,
        updated_at=role.updated_at,
    )


@router.get("/", response_model=List[RoleResponse])
async def list_roles(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission())
):
    """List all roles with their permissions and assigned-user count (admin only)."""
    roles = db.query(Role).order_by(Role.is_system.desc(), Role.name).all()
    return [_role_to_response(role) for role in roles]


@router.get("/permissions", response_model=List[PermissionResponse])
async def list_permissions(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission())
):
    """List the full permission catalog (admin only)."""
    return db.query(Permission).order_by(Permission.resource, Permission.action).all()


@router.post("/", response_model=RoleResponse, status_code=status.HTTP_201_CREATED)
async def create_role(
    role_data: RoleCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission())
):
    """Create a custom role (admin only)."""
    existing = db.query(Role).filter(Role.name == role_data.name).first()
    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Role '{role_data.name}' already exists"
        )

    permissions = []
    if role_data.permission_ids:
        permissions = db.query(Permission).filter(Permission.id.in_(role_data.permission_ids)).all()
        if len(permissions) != len(set(role_data.permission_ids)):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="One or more permission_ids are invalid"
            )

    role = Role(
        name=role_data.name,
        description=role_data.description,
        is_system=False,
        permissions=permissions,
    )
    db.add(role)
    db.commit()
    db.refresh(role)
    return _role_to_response(role)


@router.put("/{role_id}", response_model=RoleResponse)
async def update_role(
    role_id: int,
    role_update: RoleUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission())
):
    """Update a role's name/description/permissions (admin only).

    System roles cannot be renamed or have their description changed, but
    their permissions ARE editable, same as custom roles.
    """
    role = db.query(Role).filter(Role.id == role_id).first()
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")

    if role.is_system:
        if role_update.name is not None and role_update.name != role.name:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="System role name cannot be changed"
            )
        if role_update.description is not None and role_update.description != role.description:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="System role description cannot be changed"
            )
    else:
        if role_update.name is not None:
            existing = db.query(Role).filter(Role.name == role_update.name, Role.id != role_id).first()
            if existing:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Role '{role_update.name}' already exists"
                )
            role.name = role_update.name
        if role_update.description is not None:
            role.description = role_update.description

    if role_update.permission_ids is not None:
        permissions = db.query(Permission).filter(Permission.id.in_(role_update.permission_ids)).all()
        if len(permissions) != len(set(role_update.permission_ids)):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="One or more permission_ids are invalid"
            )
        role.permissions = permissions

    db.commit()
    db.refresh(role)
    return _role_to_response(role)


@router.delete("/{role_id}")
async def delete_role(
    role_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission())
):
    """Delete a custom role (admin only). System roles and roles with assigned users cannot be deleted."""
    role = db.query(Role).filter(Role.id == role_id).first()
    if not role:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")

    if role.is_system:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="System roles cannot be deleted"
        )

    user_count = len(role.users)
    if user_count > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"{user_count} user(s) still have this role — reassign them first"
        )

    role.permissions = []
    db.delete(role)
    db.commit()

    return {"message": f"Role '{role.name}' deleted successfully"}
