"""
Servizio CRUD per i preset di permessi (sottoruoli nominati).

Admin e supervisor possono creare, modificare ed eliminare preset per il proprio tenant.
"""

import uuid

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import ConflictError, ForbiddenError, NotFoundError
from app.models.permission_preset import PermissionPreset
from app.models.user import User
from app.schemas.permission_preset import PermissionPresetCreate, PermissionPresetUpdate


class PermissionPresetService:
    def __init__(self, db: AsyncSession) -> None:
        self.db = db

    def _require_supervisor_or_admin(self, user: User) -> None:
        if not user.is_supervisor_or_admin:
            raise ForbiddenError("Solo amministratori e supervisori possono gestire i preset")

    async def list_presets(self, tenant_id: uuid.UUID) -> list[PermissionPreset]:
        """Ritorna tutti i preset del tenant ordinati per nome."""
        result = await self.db.execute(
            select(PermissionPreset)
            .where(PermissionPreset.tenant_id == tenant_id)
            .order_by(PermissionPreset.name)
        )
        return list(result.scalars().all())

    async def get_preset(self, preset_id: uuid.UUID, tenant_id: uuid.UUID) -> PermissionPreset:
        """Recupera un preset per ID verificando che appartenga al tenant."""
        preset = await self.db.get(PermissionPreset, preset_id)
        if not preset or preset.tenant_id != tenant_id:
            raise NotFoundError("preset")
        return preset

    async def create_preset(
        self,
        tenant_id: uuid.UUID,
        data: PermissionPresetCreate,
        created_by: User,
    ) -> PermissionPreset:
        """Crea un nuovo preset. Il nome deve essere unico per tenant."""
        self._require_supervisor_or_admin(created_by)

        # Verifica unicita' nome
        existing = await self.db.execute(
            select(PermissionPreset).where(
                PermissionPreset.tenant_id == tenant_id,
                PermissionPreset.name == data.name,
            )
        )
        if existing.scalar_one_or_none():
            raise ConflictError(f"Esiste gia' un preset con nome '{data.name}'")

        preset = PermissionPreset(
            tenant_id=tenant_id,
            name=data.name,
            description=data.description,
            can_read=data.can_read,
            can_send=data.can_send,
            can_manage=data.can_manage,
            can_conserve=data.can_conserve,
            created_by=created_by.id,
        )
        self.db.add(preset)
        await self.db.flush()
        await self.db.refresh(preset)
        return preset

    async def update_preset(
        self,
        preset_id: uuid.UUID,
        tenant_id: uuid.UUID,
        data: PermissionPresetUpdate,
        updated_by: User,
    ) -> PermissionPreset:
        """Aggiorna un preset esistente."""
        self._require_supervisor_or_admin(updated_by)

        preset = await self.get_preset(preset_id, tenant_id)

        # Verifica unicita' nome se cambia
        if data.name is not None and data.name != preset.name:
            existing = await self.db.execute(
                select(PermissionPreset).where(
                    PermissionPreset.tenant_id == tenant_id,
                    PermissionPreset.name == data.name,
                )
            )
            if existing.scalar_one_or_none():
                raise ConflictError(f"Esiste gia' un preset con nome '{data.name}'")
            preset.name = data.name

        if data.description is not None:
            preset.description = data.description
        if data.can_read is not None:
            preset.can_read = data.can_read
        if data.can_send is not None:
            preset.can_send = data.can_send
        if data.can_manage is not None:
            preset.can_manage = data.can_manage
        if data.can_conserve is not None:
            preset.can_conserve = data.can_conserve

        await self.db.flush()
        await self.db.refresh(preset)
        return preset

    async def delete_preset(
        self,
        preset_id: uuid.UUID,
        tenant_id: uuid.UUID,
        deleted_by: User,
    ) -> None:
        """Elimina un preset."""
        self._require_supervisor_or_admin(deleted_by)
        preset = await self.get_preset(preset_id, tenant_id)
        await self.db.delete(preset)
        await self.db.flush()
