from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload

from app.models.rbac import Permission, Role
from app.schemas.rbac import RoleCreate, RolePermissionUpdate, RoleUpdate


class RbacRepository:
    @staticmethod
    def get_roles(db: Session) -> list[Role]:
        return list(
            db.scalars(
                select(Role)
                .options(selectinload(Role.permissions))
                .order_by(Role.name)
            )
        )

    @staticmethod
    def get_role_by_id(db: Session, role_id: int) -> Role | None:
        return db.scalar(
            select(Role)
            .where(Role.id == role_id)
            .options(selectinload(Role.permissions))
        )

    @staticmethod
    def get_role_by_name(db: Session, name: str) -> Role | None:
        return db.scalar(
            select(Role)
            .where(Role.name == name)
            .options(selectinload(Role.permissions))
        )

    @staticmethod
    def get_permissions(db: Session) -> list[Permission]:
        return list(db.scalars(select(Permission).order_by(Permission.module, Permission.name)))

    @staticmethod
    def get_permissions_by_ids(db: Session, permission_ids: list[int]) -> list[Permission]:
        if not permission_ids:
            return []

        return list(db.scalars(select(Permission).where(Permission.id.in_(permission_ids))))

    @staticmethod
    def create_role(db: Session, role: RoleCreate) -> Role:
        db_role = Role(
            name=role.name,
            display_name=role.display_name,
            description=role.description,
            is_system=False,
        )
        db_role.permissions = RbacRepository.get_permissions_by_ids(db, role.permission_ids)

        db.add(db_role)
        db.commit()
        db.refresh(db_role)

        return RbacRepository.get_role_by_id(db, db_role.id) or db_role

    @staticmethod
    def update_role(db: Session, db_role: Role, role: RoleUpdate) -> Role:
        db_role.name = role.name
        db_role.display_name = role.display_name
        db_role.description = role.description
        db_role.permissions = RbacRepository.get_permissions_by_ids(db, role.permission_ids)

        db.commit()
        db.refresh(db_role)

        return RbacRepository.get_role_by_id(db, db_role.id) or db_role

    @staticmethod
    def update_role_permissions(
        db: Session,
        db_role: Role,
        permission_update: RolePermissionUpdate,
    ) -> Role:
        db_role.permissions = RbacRepository.get_permissions_by_ids(
            db,
            permission_update.permission_ids,
        )

        db.commit()
        db.refresh(db_role)

        return RbacRepository.get_role_by_id(db, db_role.id) or db_role

    @staticmethod
    def delete_role(db: Session, db_role: Role) -> None:
        db.delete(db_role)
        db.commit()
