"""
Permission repository for database operations and seeding.
"""

from uuid import UUID

from sqlalchemy import select
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.core.permissions import ROLE_DEFAULT_PERMISSIONS, PermissionCode
from app.models.user import (
    Permission,
    RolePermission,
    UserPermissionOverride,
    UserRole,
)


class PermissionRepository:
    """Repository handling Permission, RolePermission, and UserPermissionOverride entities."""

    async def get_by_code(self, db: AsyncSession, code: str) -> Permission | None:
        """Fetch permission definition by unique code."""
        stmt = select(Permission).where(Permission.code == code)
        res = await db.execute(stmt)
        return res.scalars().first()

    async def get_role_permissions(self, db: AsyncSession, role: UserRole) -> list[RolePermission]:
        """Fetch all role permission mappings for a given role."""
        stmt = select(RolePermission).where(RolePermission.role == role)
        res = await db.execute(stmt)
        return list(res.scalars().all())

    async def get_user_overrides(self, db: AsyncSession, user_id: UUID) -> list[UserPermissionOverride]:
        """Fetch all permission overrides for a user."""
        stmt = (
            select(UserPermissionOverride)
            .where(UserPermissionOverride.user_id == user_id)
            .options(selectinload(UserPermissionOverride.permission))
        )
        res = await db.execute(stmt)
        return list(res.scalars().all())

    async def seed_permissions(self, db: AsyncSession) -> None:
        """
        Idempotently seed all PermissionCode definitions and role default mappings.

        INSERT-ONLY: never overwrites an existing RolePermission.default_granted.
        Superadmin toggles in the permission matrix must survive backend restarts —
        resetting them here was silently discarding every customization.
        New permission codes get their code-default grant at insert time only.
        """
        # PostgreSQL inserts batches atomically and keeps existing administrator
        # choices on conflict, including concurrent worker startup/discovery.
        await db.execute(
            insert(Permission)
            .values(
                [
                    {"code": code.value, "description": f"Allows {code.value.replace(':', ' ')} operations"}
                    for code in PermissionCode
                ]
            )
            .on_conflict_do_nothing(index_elements=[Permission.code])
        )
        permissions = (await db.execute(select(Permission))).scalars().all()
        perm_map = {permission.code: permission.id for permission in permissions}
        await db.execute(
            insert(RolePermission)
            .values(
                [
                    {
                        "role": role.value,
                        "permission_id": perm_map[code.value],
                        "default_granted": code in allowed_codes,
                    }
                    for role, allowed_codes in ROLE_DEFAULT_PERMISSIONS.items()
                    for code in PermissionCode
                ]
            )
            .on_conflict_do_nothing(index_elements=[RolePermission.role, RolePermission.permission_id])
        )
        await db.commit()
