"""
Configuración centralizada de la aplicación usando Pydantic Settings
"""

from typing import List, Literal
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Configuración de la aplicación"""

    # Application
    APP_NAME: str = "Mi Aplicación"
    # Sobreescribible con APP_VERSION (Dockerfile/Jenkins: build-arg + ENV) o entorno en runtime
    APP_VERSION: str = "dev-local"
    DEBUG: bool = True
    ENVIRONMENT: str = "development"

    # Auth local para desarrollo (sin Keycloak/Hub)
    # Nunca activar en producción.
    DEV_LOCAL_AUTH: bool = False
    DEV_LOCAL_AUTH_EMAIL: str = "dev@local.test"
    DEV_LOCAL_AUTH_NAME: str = "Dev User"
    DEV_LOCAL_AUTH_TENANT_ID: int = 1
    DEV_LOCAL_AUTH_COMPANY_ID: int = 1

    # Database - Core (Shared)
    CORE_DB_HOST: str = "postgres"
    CORE_DB_PORT: int = 5432
    CORE_DB_NAME: str = "app_core"
    CORE_DB_USER: str = "postgres"
    CORE_DB_PASSWORD: str = "postgres"

    # Security
    SECRET_KEY: str = "change-this-secret-key-in-production"
    ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30

    # Valkey / Redis
    VALKEY_URL: str = "redis://valkey:6379/0"
    PERMISSION_CACHE_ENABLED: bool = True
    PERMISSION_CACHE_TTL_SECONDS: int = 300

    # Sesión local del CRM (patrón SIWEB) — desacopla la sesión de la app del
    # token KC de 60s. Tras SSO/login se guardan los tokens KC en valkey y se emite
    # una sesión local firmada (HS256) con vida por inactividad (idle) y cap
    # absoluto. Así el refresh del token KC contra el Hub solo se intenta al expirar
    # la sesión local (no cada ~60s), lo que elimina el bucle de login.
    #
    # SE RESPETA la revocación central de Keycloak: si el Hub rechaza el refresh, la
    # sesión termina (no hay re-emisión local de fallback). Flag-gated para rollback:
    # con SESSION_STORE_ENABLED=False el comportamiento no cambia.
    SESSION_STORE_ENABLED: bool = False
    SESSION_IDLE_MINUTES: int = 30
    SESSION_MAX_HOURS: int = 10

    # Synchronization
    SYNC_SECRET_TOKEN: str = "change-this-sync-token-in-production"
    CENTRAL_SERVER_URL: str = "http://localhost:8000/api/v1/core/help-center/sync/"
    SPOKE_URLS: str = "" # Comma separated list of Spoke URLs for Broadcast (Hub only)

    # CORS
    CORS_ORIGINS: str = "http://localhost:5173,http://localhost:3000"

    # Hub de Aduanasoft — requerido siempre (SaaS y self-hosted)
    HUB_URL: str = "http://localhost:8001"
    # Base API del Hub/Workspace para endpoint /v1/auth/me (fuente de verdad de perfil)
    HUB_API_BASE_URL: str = ""
    HUB_PROFILE_SYNC_TIMEOUT_MS: int = 3000
    # Cuenta de servicio Hub — usada para operaciones admin (ej. sync de nombre a Keycloak)
    HUB_ADMIN_EMAIL: str = ""
    HUB_ADMIN_PASSWORD: str = ""

    # URL pública del frontend — usada en links de email (invitaciones, etc.)
    APP_PUBLIC_URL: str = "http://localhost:3000"

    @field_validator("CENTRAL_SERVER_URL", "SPOKE_URLS", "HUB_URL", "HUB_API_BASE_URL", "EFC_API_URL", mode="before")
    @classmethod
    def strip_quotes(cls, v: str) -> str:
        if v and isinstance(v, str):
            v = v.strip().strip('"').strip("'")
            # Evitar que solo espacios en .env se conviertan en "/" (rompe httpx: falta protocolo).
            if not v:
                return ""
            if not v.endswith("/"):
                v += "/"
            return v
        return v
        
    # External APIs
    SITAR_API_URL: str = "api.sitar.aduanasoft.com:880"
    COVE_API_URL: str = "https://api.vu.aduanasoft.com"
    COVE_API_VERIFY_SSL: bool = False
    COVE_FIEL_HASH_KEY: str = ""
    COVE_FIEL_HASH_IV: str = ""
    SITAR_API_USER: str = ""
    SITAR_API_PASSWORD: str = ""
    # SMTP Email Configuration
    SMTP_HOST: str = "smtp.gmail.com"
    SMTP_PORT: int = 587
    SMTP_USER: str = ""
    SMTP_PASSWORD: str = ""
    SMTP_FROM_NAME: str = "Mi Aplicación"
    SMTP_USE_TLS: bool = True

    # CSV imports (layouts_csv): redis = base64 en Valkey; minio = S3 + referencia en Redis
    CSV_IMPORT_STORAGE: Literal["redis", "minio"] = "minio"
    S3_ENDPOINT_URL: str = "http://minio:9000"
    S3_ACCESS_KEY: str = ""
    S3_SECRET_KEY: str = ""
    S3_BUCKET: str = "app"
    S3_REGION: str = "us-east-1"
    S3_USE_SSL: bool = False
    # Logos, certificados, help (si no quieres MinIO aquí, pon false Y CSV_IMPORT_STORAGE=redis)
    S3_FILE_STORAGE: bool = True
    S3_PRESIGNED_EXPIRES_SECONDS: int = 3600

    # ----- PAC Comercio Digital (timbrado CFDI) -----
    # El modo NO se configura aquí: vive en fin.invoices.stamping_mode, por factura. Esta
    # variable sólo define con qué valor nacen las facturas que no lo especifican.
    PAC_DEFAULT_MODE: Literal["pruebas", "produccion"] = "pruebas"
    PAC_HOST_TEST: str = "pruebas.comercio-digital.mx"
    PAC_HOST_PROD: str = "ws.comercio-digital.mx"
    PAC_USER: str = ""       # header usrws
    PAC_PASSWORD: str = ""   # header pwdws
    PAC_TIMEOUT_SECONDS: int = 15
    PAC_NOTIFICATION_EMAIL: str = ""  # header email, opcional
    # Clave maestra que cifra las contraseñas de los CSD guardadas en la base (ver core/crypto).
    # Sin ella no se pueden cargar certificados. Se genera con:
    #   python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
    CSD_ENCRYPTION_KEY: str = ""
    # Contraseña global del CSD. LEGADO: sólo se usa como respaldo si una empresa no tiene su
    # propia contraseña guardada. Lo correcto es cargar el CSD por empresa.
    CSD_PASSWORD: str = ""

    # ── EFC (expediente electrónico) ────────────────────────────────────────────────────────
    # Carril CRM -> EFC: los documentos del CRM se resguardan en el expediente de EFC.
    # Los nombres son los MISMOS que usa el gateway de Anexo22 contra el mismo EFC: inventar
    # otros obligaría a quien opera los dos sistemas a recordar dos juegos de variables para
    # exactamente lo mismo.
    # EFC_API_URL vacía = integración APAGADA. Todo el enganche es best-effort y hace no-op:
    # el CRM sigue funcionando igual, guardando los archivos solo en su MinIO.
    EFC_API_URL: str = ""
    EFC_API_KEY: str = ""              # == CRM_INTEGRATION_API_KEY del lado de EFC
    EFC_API_VERIFY_SSL: bool = True
    EFC_API_TIMEOUT_MS: int = 8000     # metadatos: resolver, ingest, completar
    # Las subidas van aparte: 8 s no alcanzan para un archivo de 25 MB. Debe quedar POR DEBAJO
    # del proxy_read_timeout del nginx de EFC (ver core/efc_client.py).
    EFC_UPLOAD_TIMEOUT_MS: int = 55000
    # Scaffolding de mTLS: activarlo es configuración, no código.
    EFC_MTLS_CA_PATH: str = ""
    EFC_MTLS_CERT_PATH: str = ""
    EFC_MTLS_KEY_PATH: str = ""

    model_config = SettingsConfigDict(
        env_file=[".env", "../.env"],
        case_sensitive=True,
        extra="ignore",
        env_file_encoding="utf-8",
    )

    @property
    def core_database_url(self) -> str:
        """URL de conexión a la base de datos core"""
        return f"postgresql://{self.CORE_DB_USER}:{self.CORE_DB_PASSWORD}@{self.CORE_DB_HOST}:{self.CORE_DB_PORT}/{self.CORE_DB_NAME}"

    @property
    def async_core_database_url(self) -> str:
        """URL de conexión asíncrona a la base de datos core"""
        return f"postgresql+asyncpg://{self.CORE_DB_USER}:{self.CORE_DB_PASSWORD}@{self.CORE_DB_HOST}:{self.CORE_DB_PORT}/{self.CORE_DB_NAME}"

    @property
    def cors_origins_list(self) -> List[str]:
        """Lista de orígenes CORS permitidos"""
        return [origin.strip() for origin in self.CORS_ORIGINS.split(",")]

    @property
    def use_s3_object_storage(self) -> bool:
        """
        Usar MinIO para logos, certificados y Help (mismo bucket que CSV).
        True si los imports CSV ya usan MinIO o si S3_FILE_STORAGE está activo.
        """
        return self.CSV_IMPORT_STORAGE == "minio" or self.S3_FILE_STORAGE

    @property
    def hub_api_base_url(self) -> str:
        """
        Base URL para endpoints /v1 del Workspace/Hub.
        Si HUB_API_BASE_URL no está definido, deriva de HUB_URL + /api.
        """
        custom = (self.HUB_API_BASE_URL or "").strip().rstrip("/")
        if custom:
            return custom
        return f"{self.HUB_URL.rstrip('/')}/api"


# Instancia global de configuración
settings = Settings()
