"""
Service per la gestione della rubrica indirizzi PEC (Feature 6).
"""

import csv
import io
import uuid

from sqlalchemy import func, or_, select
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import ConflictError, NotFoundError
from app.models.pec_contact import PecContact
from app.schemas.pec_contact import (
    PecContactCreate,
    PecContactImportResult,
    PecContactUpdate,
)


class PecContactService:
    def __init__(self, db: AsyncSession):
        self.db = db

    async def list_contacts(
        self,
        tenant_id: uuid.UUID,
        q: str | None = None,
        page: int = 1,
        page_size: int = 50,
    ) -> tuple[list[PecContact], int]:
        query = select(PecContact).where(PecContact.tenant_id == tenant_id)
        if q:
            pattern = f"%{q.lower()}%"
            query = query.where(
                or_(
                    PecContact.email.ilike(pattern),
                    PecContact.name.ilike(pattern),
                    PecContact.organization.ilike(pattern),
                )
            )
        query = query.order_by(PecContact.is_favorite.desc(), PecContact.name, PecContact.email)

        count_q = select(func.count()).select_from(query.subquery())
        total = (await self.db.execute(count_q)).scalar_one()

        offset = (page - 1) * page_size
        query = query.offset(offset).limit(page_size)
        items = list((await self.db.execute(query)).scalars().all())
        return items, total

    async def get_contact(
        self, tenant_id: uuid.UUID, contact_id: uuid.UUID
    ) -> PecContact:
        result = await self.db.execute(
            select(PecContact).where(
                PecContact.id == contact_id,
                PecContact.tenant_id == tenant_id,
            )
        )
        contact = result.scalar_one_or_none()
        if not contact:
            raise NotFoundError(f"Contatto {contact_id} non trovato")
        return contact

    async def create_contact(
        self,
        tenant_id: uuid.UUID,
        data: PecContactCreate,
        created_by: uuid.UUID | None = None,
    ) -> PecContact:
        email = data.email.lower().strip()
        # Verifica unicita'
        existing = await self.db.execute(
            select(PecContact).where(
                PecContact.tenant_id == tenant_id,
                PecContact.email == email,
            )
        )
        if existing.scalar_one_or_none():
            raise ConflictError(f"Contatto '{email}' gia' esistente")

        contact = PecContact(
            tenant_id=tenant_id,
            email=email,
            name=data.name,
            organization=data.organization,
            notes=data.notes,
            is_favorite=data.is_favorite,
            auto_saved=False,
            created_by=created_by,
        )
        self.db.add(contact)
        await self.db.commit()
        await self.db.refresh(contact)
        return contact

    async def update_contact(
        self,
        tenant_id: uuid.UUID,
        contact_id: uuid.UUID,
        data: PecContactUpdate,
    ) -> PecContact:
        contact = await self.get_contact(tenant_id, contact_id)
        if data.name is not None:
            contact.name = data.name
        if data.organization is not None:
            contact.organization = data.organization
        if data.notes is not None:
            contact.notes = data.notes
        if data.is_favorite is not None:
            contact.is_favorite = data.is_favorite
        await self.db.commit()
        await self.db.refresh(contact)
        return contact

    async def delete_contact(
        self, tenant_id: uuid.UUID, contact_id: uuid.UUID
    ) -> None:
        contact = await self.get_contact(tenant_id, contact_id)
        await self.db.delete(contact)
        await self.db.commit()

    async def auto_save_sender(
        self,
        tenant_id: uuid.UUID,
        email: str,
    ) -> None:
        """
        Salva automaticamente il mittente nella rubrica se non esiste ancora.
        Operazione non bloccante: gli errori vengono ignorati silenziosamente.

        Usata dal worker IMAP durante la sincronizzazione dei messaggi inbound.
        """
        if not email:
            return
        email = email.lower().strip()
        try:
            # Upsert: inserisce solo se non esiste
            stmt = insert(PecContact).values(
                id=uuid.uuid4(),
                tenant_id=tenant_id,
                email=email,
                auto_saved=True,
            ).on_conflict_do_nothing(
                constraint="uq_pec_contact_email_tenant"
            )
            await self.db.execute(stmt)
            await self.db.commit()
        except Exception:
            await self.db.rollback()

    async def import_csv(
        self,
        tenant_id: uuid.UUID,
        csv_content: str,
        created_by: uuid.UUID | None = None,
    ) -> PecContactImportResult:
        """
        Importa contatti da un CSV con colonne: email, name, organization.

        Aggiorna i record esistenti con name/organization se forniti.
        """
        result = PecContactImportResult(created=0, updated=0, skipped=0)
        reader = csv.DictReader(io.StringIO(csv_content))

        for row_num, row in enumerate(reader, start=2):
            email = row.get("email", "").strip().lower()
            if not email or "@" not in email:
                result.skipped += 1
                result.errors.append(f"Riga {row_num}: email non valida '{email}'")
                continue

            name = row.get("name", "").strip() or None
            organization = row.get("organization", "").strip() or None

            try:
                existing = await self.db.execute(
                    select(PecContact).where(
                        PecContact.tenant_id == tenant_id,
                        PecContact.email == email,
                    )
                )
                contact = existing.scalar_one_or_none()

                if contact:
                    # Aggiorna solo se i campi erano vuoti (non sovrascrive dati manuali)
                    updated = False
                    if name and not contact.name:
                        contact.name = name
                        updated = True
                    if organization and not contact.organization:
                        contact.organization = organization
                        updated = True
                    if updated:
                        result.updated += 1
                    else:
                        result.skipped += 1
                else:
                    contact = PecContact(
                        tenant_id=tenant_id,
                        email=email,
                        name=name,
                        organization=organization,
                        auto_saved=False,
                        created_by=created_by,
                    )
                    self.db.add(contact)
                    result.created += 1
            except Exception as e:
                result.errors.append(f"Riga {row_num} ({email}): {e}")
                result.skipped += 1

        await self.db.commit()
        return result

    async def search_for_autocomplete(
        self,
        tenant_id: uuid.UUID,
        q: str,
        limit: int = 10,
    ) -> list[PecContact]:
        """Ricerca veloce per autocomplete nel compose."""
        if not q or len(q) < 2:
            return []
        pattern = f"%{q.lower()}%"
        result = await self.db.execute(
            select(PecContact)
            .where(
                PecContact.tenant_id == tenant_id,
                or_(
                    PecContact.email.ilike(pattern),
                    PecContact.name.ilike(pattern),
                )
            )
            .order_by(PecContact.is_favorite.desc(), PecContact.email)
            .limit(limit)
        )
        return list(result.scalars().all())
