from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.role import AksiEnum, ModulEnum, Role, RolePermission
from app.models.user import User
from app.repositories import role_repository
from app.schemas.role import PermissionItem, RoleCreate, RoleUpdate


async def list_roles(db: AsyncSession, spbu_id: int | None = None) -> list[Role]:
    return await role_repository.get_all(db, spbu_id)


async def create_role(db: AsyncSession, data: RoleCreate, actor: User) -> Role:
    if data.spbu_id is not None and not actor.is_superadmin:
        assigned_ids = {a.spbu_id for a in actor.assignments}
        if data.spbu_id not in assigned_ids:
            raise PermissionError("Tidak punya akses ke SPBU ini")
    elif data.spbu_id is None and not actor.is_superadmin:
        raise PermissionError("Hanya Super Admin yang dapat membuat role global")
    try:
        result = await role_repository.create(
            db,
            nama=data.nama,
            deskripsi=data.deskripsi,
            spbu_id=data.spbu_id,
            is_system=False,
        )
        await db.commit()
        return result
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data role konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise


async def get_role(db: AsyncSession, role_id: int) -> Role:
    role = await role_repository.get_by_id(db, role_id)
    if not role:
        raise ValueError("Role tidak ditemukan")
    return role


async def update_role(
    db: AsyncSession, role_id: int, data: RoleUpdate, actor: User
) -> Role:
    role = await get_role(db, role_id)
    update_data = data.model_dump(exclude_none=True)
    # System roles: nama/deskripsi locked, but flags (can_be_scheduled, can_login_web) are editable
    if role.is_system:
        update_data.pop("nama", None)
        update_data.pop("deskripsi", None)
        if not update_data:
            raise PermissionError("Nama dan deskripsi role sistem tidak dapat diubah")
    try:
        result = await role_repository.update(db, role, **update_data)
        await db.commit()
        return result
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data role konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise


async def delete_role(db: AsyncSession, role_id: int, actor: User) -> None:
    role = await get_role(db, role_id)
    if role.is_system:
        raise PermissionError("Role sistem tidak dapat dihapus")
    try:
        await role_repository.delete_role(db, role, hard_delete=False)
        await db.commit()
    except SQLAlchemyError:
        await db.rollback()
        raise


async def get_permissions(
    db: AsyncSession, role_id: int
) -> list[RolePermission]:
    await get_role(db, role_id)
    return await role_repository.get_permissions(db, role_id)


async def set_permissions(
    db: AsyncSession,
    role_id: int,
    permissions: list[PermissionItem],
    actor: User,
) -> list[RolePermission]:
    role = await get_role(db, role_id)
    # Semua role (termasuk system) bisa diubah permissionnya, tapi tidak bisa dihapus
    perm_tuples = [(p.modul, p.aksi) for p in permissions]
    try:
        result = await role_repository.set_permissions(db, role_id, perm_tuples)
        await db.commit()
        return result
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data permission konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise
