"""
Schema Pydantic per TenantSettings – lettura e aggiornamento impostazioni tenant.
Include schemi per il modulo di indicizzazione full-text.
"""

import uuid
from datetime import datetime
from typing import Literal, Optional

from pydantic import BaseModel, Field, field_validator


ArchivalMode = Literal["mock", "production"]


class TenantSettingsResponse(BaseModel):
    """
    Risposta GET /settings.

    Le credenziali del conservatore non vengono mai esposte in chiaro:
    vengono restituite come flag booleani (is_configured).
    """

    id: uuid.UUID
    tenant_id: uuid.UUID

    # Archiviazione
    archival_mode: ArchivalMode
    conservatore_id: str
    conservatore_endpoint: str | None
    conservatore_username_configured: bool   # TRUE se la username è già salvata
    conservatore_password_configured: bool   # TRUE se la password è già salvata
    archival_notes: str | None

    created_at: datetime
    updated_at: datetime

    model_config = {"from_attributes": True}


class TenantSettingsUpdate(BaseModel):
    """
    Body PUT /settings.

    Tutti i campi sono opzionali (PATCH semantics).
    Le credenziali vengono aggiornate solo se fornite.
    Un valore esplicitamente None può essere usato per cancellare le credenziali.
    """

    archival_mode: ArchivalMode | None = None

    conservatore_id: str | None = Field(
        default=None, min_length=1, max_length=100
    )

    # URL endpoint del conservatore (obbligatorio in produzione, ignorato in mock)
    conservatore_endpoint: str | None = None

    # Credenziali in chiaro: vengono cifrate prima del salvataggio.
    # Valore stringa vuota ("") = cancella la credenziale.
    conservatore_username: str | None = None
    conservatore_password: str | None = None

    archival_notes: str | None = None

    @field_validator("archival_mode")
    @classmethod
    def validate_archival_mode(cls, v: str | None) -> str | None:
        if v is not None and v not in ("mock", "production"):
            raise ValueError("archival_mode deve essere 'mock' o 'production'")
        return v


# ─── Schemi indicizzazione full-text ──────────────────────────────────────────

class IndexingStats(BaseModel):
    """Statistiche di copertura dell'indicizzazione per un tenant."""

    total_messages: int
    indexed_messages: int
    unindexed_messages: int
    coverage_pct: float          # percentuale messaggi con search_vector != NULL

    attachments_total: int       # allegati PDF/DOCX totali
    attachments_extracted: int   # allegati con testo estratto
    attachments_pct: float       # percentuale allegati con testo estratto


class IndexingJobStatus(BaseModel):
    """Stato di un job di reindex in corso o completato."""

    status: str                        # idle | running | completed | failed | cancelled
    mode: Optional[str] = None         # full | differential
    total: int = 0
    processed: int = 0
    progress_pct: float = 0.0

    started_at: Optional[str] = None   # ISO datetime
    finished_at: Optional[str] = None  # ISO datetime
    started_by: Optional[str] = None   # email utente che ha avviato il job
    elapsed_seconds: Optional[int] = None
    is_stale: bool = False             # True se running da piu' di STALE_THRESHOLD_HOURS
    error: Optional[str] = None


class StartReindexRequest(BaseModel):
    """Body per POST /settings/indexing/reindex."""

    mode: Literal["full", "differential"] = "differential"


class StartRescanRequest(BaseModel):
    """Body per POST /settings/indexing/rescan."""

    force: bool = Field(
        default=False,
        description=(
            "False (default): estrae solo allegati con extracted_text NULL. "
            "True: ri-estrae tutti gli allegati, sovrascrivendo i testi gia' presenti."
        ),
    )
