"""
Servizio TenantSettings – lettura e aggiornamento configurazione per-tenant.

Responsabilità:
- Upsert della riga settings (crea se non esiste, aggiorna altrimenti)
- Cifratura/decifratura AES-256-GCM delle credenziali conservatore
- Validazione di consistenza (produzione richiede endpoint + credenziali)
"""

import uuid

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

from app.core.exceptions import BadRequestError
from app.core.security import decrypt_credential, encrypt_credential
from app.models.tenant_settings import TenantSettings
from app.schemas.tenant_settings import TenantSettingsResponse, TenantSettingsUpdate


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

    # ─── Lettura (o creazione defaults) ──────────────────────────────────────

    async def get_or_create(self, tenant_id: uuid.UUID) -> TenantSettings:
        """
        Restituisce le impostazioni del tenant.
        Se non esistono, crea una riga con i valori di default (mock).
        """
        result = await self.db.execute(
            select(TenantSettings).where(TenantSettings.tenant_id == tenant_id)
        )
        settings = result.scalar_one_or_none()

        if settings is None:
            settings = TenantSettings(
                tenant_id=tenant_id,
                archival_mode="mock",
                conservatore_id="mock",
            )
            self.db.add(settings)
            await self.db.flush()

        return settings

    # ─── Aggiornamento ────────────────────────────────────────────────────────

    async def update(
        self,
        tenant_id: uuid.UUID,
        data: TenantSettingsUpdate,
    ) -> TenantSettings:
        """
        Aggiornamento parziale delle impostazioni.

        Regole:
        - Il passaggio a 'production' è consentito solo se conservatore_endpoint
          è già configurato oppure viene fornito nel body corrente.
        - Le credenziali vengono cifrate prima del salvataggio.
        - Una stringa vuota "" su username/password cancella la credenziale.
        """
        settings = await self.get_or_create(tenant_id)

        # Applica aggiornamenti campi non-credenziali
        if data.archival_mode is not None:
            settings.archival_mode = data.archival_mode

        if data.conservatore_id is not None:
            settings.conservatore_id = data.conservatore_id

        if data.conservatore_endpoint is not None:
            settings.conservatore_endpoint = data.conservatore_endpoint or None

        if data.archival_notes is not None:
            settings.archival_notes = data.archival_notes or None

        # Aggiorna credenziali (cifra se fornite, cancella se stringa vuota)
        if data.conservatore_username is not None:
            if data.conservatore_username == "":
                settings.conservatore_username_enc = None
            else:
                settings.conservatore_username_enc = encrypt_credential(
                    data.conservatore_username
                )

        if data.conservatore_password is not None:
            if data.conservatore_password == "":
                settings.conservatore_password_enc = None
            else:
                settings.conservatore_password_enc = encrypt_credential(
                    data.conservatore_password
                )

        # Validazione: produzione richiede endpoint configurato
        if settings.archival_mode == "production":
            if not settings.conservatore_endpoint:
                raise BadRequestError(
                    "La modalità produzione richiede un endpoint conservatore valido. "
                    "Inserisci l'URL prima di attivare la modalità produzione."
                )

        await self.db.flush()
        return settings

    # ─── Costruzione response ─────────────────────────────────────────────────

    @staticmethod
    def to_response(settings: TenantSettings) -> TenantSettingsResponse:
        """
        Converte il modello in DTO di risposta.
        Le credenziali NON vengono mai esposte in chiaro.
        """
        return TenantSettingsResponse(
            id=settings.id,
            tenant_id=settings.tenant_id,
            archival_mode=settings.archival_mode,  # type: ignore[arg-type]
            conservatore_id=settings.conservatore_id,
            conservatore_endpoint=settings.conservatore_endpoint,
            conservatore_username_configured=settings.conservatore_username_enc is not None,
            conservatore_password_configured=settings.conservatore_password_enc is not None,
            archival_notes=settings.archival_notes,
            created_at=settings.created_at,
            updated_at=settings.updated_at,
        )

    # ─── Helper per il worker (recupera credenziali decifrate) ───────────────

    async def get_conservatore_credentials(
        self, tenant_id: uuid.UUID
    ) -> dict:
        """
        Restituisce le credenziali del conservatore decifrate.
        Usato dal worker per effettuare versamenti.
        """
        settings = await self.get_or_create(tenant_id)
        return {
            "mode": settings.archival_mode,
            "conservatore_id": settings.conservatore_id,
            "endpoint": settings.conservatore_endpoint,
            "username": (
                decrypt_credential(settings.conservatore_username_enc)
                if settings.conservatore_username_enc
                else None
            ),
            "password": (
                decrypt_credential(settings.conservatore_password_enc)
                if settings.conservatore_password_enc
                else None
            ),
        }
