"""Canonical configuration surface for Sardis services."""
from __future__ import annotations

from functools import lru_cache
from pathlib import Path
from typing import List, Literal, Optional

from pydantic import Field, field_validator
from pydantic_settings import BaseSettings


def _normalize_environment(value: str) -> str:
    """Normalize common environment aliases to SardisSettings.environment values."""
    v = (value or "").strip().lower()
    if v in ("development", "dev", "local"):
        return "dev"
    if v in ("production", "prod"):
        return "prod"
    if v in ("sandbox", "staging"):
        # Sardis currently uses "sandbox" as a pre-prod environment for demos.
        return "sandbox"
    return v


def _normalize_chain_mode(value: str) -> str:
    """Normalize common chain mode aliases to SardisSettings.chain_mode values."""
    v = (value or "").strip().lower()
    if v in ("sim", "simulate", "simulated"):
        return "simulated"
    # Historical/compose aliases: "testnet"/"mainnet" means "live execution"
    if v in ("live", "testnet", "mainnet"):
        return "live"
    return v


class TurnkeyConfig(BaseSettings):
    """Turnkey MPC provider configuration."""
    organization_id: str = ""
    api_public_key: str = ""
    api_private_key: str = ""  # Can be hex-encoded key or path to PEM file
    default_wallet_id: str = ""
    api_base: str = "https://api.turnkey.com"
    
    class Config:
        env_prefix = "TURNKEY_"


class MPCProvider(BaseSettings):
    """MPC provider configuration (Turnkey or Fireblocks)."""
    name: Literal["turnkey", "fireblocks", "simulated"] = "simulated"
    api_base: str = ""
    credential_id: str = ""  # Organization ID for Turnkey


class ChainConfig(BaseSettings):
    """Blockchain network configuration."""
    name: str = "base_sepolia"
    rpc_url: str = "https://sepolia.base.org"
    chain_id: int = 84532
    stablecoins: List[str] = Field(default_factory=lambda: ["USDC"])
    settlement_vault: str = ""


class SardisSettings(BaseSettings):
    """Main Sardis configuration."""
    
    # Environment
    environment: Literal["dev", "sandbox", "prod"] = "dev"
    
    # API
    api_base_url: str = "http://localhost:8000"
    
    # CORS - allowed origins for API (use str to handle comma-separated env vars)
    allowed_origins: str = "http://localhost:3005,http://localhost:5173"
    # Mandate domain allowlist
    allowed_domains: list[str] = Field(default_factory=lambda: ["sardis.network", "localhost"])
    
    @property
    def allowed_origins_list(self) -> List[str]:
        """Return allowed origins as a list."""
        if not self.allowed_origins:
            return ["http://localhost:3005", "http://localhost:5173"]
        return [o.strip() for o in self.allowed_origins.split(",") if o.strip()]
    
    # Security
    secret_key: str = ""
    
    # Mandate settings
    mandate_ttl_seconds: int = 300
    
    # Database - PostgreSQL for production
    database_url: str = ""
    ledger_dsn: str = ""
    mandate_archive_dsn: str = "sqlite:///./data/mandates.db"
    replay_cache_dsn: str = "sqlite:///./data/replay_cache.db"
    
    # Redis/Upstash for caching (optional)
    redis_url: str = ""
    
    # Chain configuration - defaults for demo
    chains: List[ChainConfig] = Field(default_factory=lambda: [
        ChainConfig(
            name="base_sepolia",
            rpc_url="https://sepolia.base.org",
            chain_id=84532,
            stablecoins=["USDC"],
            settlement_vault="",
        )
    ])
    
    # MPC provider - simulated by default for demo
    mpc: MPCProvider = Field(default_factory=MPCProvider)
    
    # Turnkey configuration (loaded from TURNKEY_* env vars)
    turnkey: TurnkeyConfig = Field(default_factory=TurnkeyConfig)
    
    # Chain execution mode
    chain_mode: Literal["simulated", "live"] = "simulated"

    @property
    def is_production(self) -> bool:
        return self.environment == "prod"
    
    @property
    def turnkey_configured(self) -> bool:
        """Check if Turnkey is properly configured."""
        return bool(
            self.turnkey.organization_id
            and self.turnkey.api_public_key
            and self.turnkey.api_private_key
        )

    class Config:
        env_prefix = "SARDIS_"
        env_nested_delimiter = "__"
        env_file = ".env"
        extra = "ignore"

    @field_validator("environment", mode="before")
    @classmethod
    def normalize_environment(cls, v: str) -> str:
        if not isinstance(v, str):
            return v
        return _normalize_environment(v)

    @field_validator("chain_mode", mode="before")
    @classmethod
    def normalize_chain_mode(cls, v: str) -> str:
        if not isinstance(v, str):
            return v
        return _normalize_chain_mode(v)

    @field_validator("secret_key")
    @classmethod
    def validate_secret_key(cls, v: str) -> str:
        import os
        env = _normalize_environment(os.getenv("SARDIS_ENVIRONMENT", "dev"))
        if env != "dev" and (not v or len(v) < 32):
            raise ValueError(
                "SECRET_KEY must be at least 32 characters in production. "
                "Generate with: python -c \"import secrets; print(secrets.token_urlsafe(32))\""
            )
        return v or "dev-only-secret-key-not-for-production"
    
    @field_validator("database_url", "ledger_dsn", mode="before")
    @classmethod
    def set_database_defaults(cls, v: str, info) -> str:
        """Use DATABASE_URL for ledger_dsn if not set."""
        import os
        import warnings
        
        if not v:
            # Try DATABASE_URL first
            v = os.getenv("DATABASE_URL", "postgresql://localhost/sardis")
        
        # Warn if using SQLite in production
        env = _normalize_environment(os.getenv("SARDIS_ENVIRONMENT", "dev"))
        if env == "prod" and v.startswith("sqlite"):
            warnings.warn(
                "SQLite is not recommended for production. "
                "Please use PostgreSQL by setting DATABASE_URL environment variable.",
                RuntimeWarning,
            )
        
        return v
    
    @field_validator("mandate_archive_dsn", "replay_cache_dsn", mode="before")
    @classmethod
    def set_cache_defaults(cls, v: str, info) -> str:
        """Use DATABASE_URL for archive/cache DSNs if PostgreSQL is available."""
        import os
        
        if not v:
            database_url = os.getenv("DATABASE_URL", "")
            if database_url.startswith(("postgresql://", "postgres://")):
                return database_url
            return v
        
        # Use DATABASE_URL if v is default SQLite and PostgreSQL is available
        if v.startswith("sqlite:") and info.field_name in ("mandate_archive_dsn", "replay_cache_dsn"):
            database_url = os.getenv("DATABASE_URL", "")
            if database_url.startswith(("postgresql://", "postgres://")):
                return database_url
        
        return v


def validate_production_config(settings: SardisSettings) -> list[str]:
    """
    Validate that all required configuration is present for production.

    Returns a list of missing/invalid configuration items.
    """
    import os
    errors = []

    if settings.environment in ("prod", "sandbox"):
        # Required for production
        if not settings.secret_key or len(settings.secret_key) < 32:
            errors.append("SARDIS_SECRET_KEY: Must be at least 32 characters")

        if not os.getenv("JWT_SECRET_KEY"):
            errors.append("JWT_SECRET_KEY: Required for auth (min 32 chars)")

        if not os.getenv("SARDIS_ADMIN_PASSWORD"):
            errors.append("SARDIS_ADMIN_PASSWORD: Required for admin access")

        if not settings.database_url or settings.database_url.startswith("sqlite"):
            errors.append("DATABASE_URL: PostgreSQL required for production")

        if not (os.getenv("REDIS_URL") or os.getenv("SARDIS_REDIS_URL") or os.getenv("UPSTASH_REDIS_URL")):
            errors.append("REDIS_URL: Required for distributed rate limiting (or set SARDIS_REDIS_URL/UPSTASH_REDIS_URL)")

        # MPC configuration
        if settings.chain_mode == "live":
            if not settings.turnkey_configured:
                errors.append("TURNKEY_*: MPC provider not configured for live mode")

        # Compliance providers (recommended)
        if not os.getenv("PERSONA_API_KEY"):
            errors.append("PERSONA_API_KEY: KYC provider not configured (recommended)")

        if not os.getenv("ELLIPTIC_API_KEY"):
            errors.append("ELLIPTIC_API_KEY: Sanctions screening not configured (recommended)")

    return errors


@lru_cache
def load_settings(env_file: str | None = None) -> SardisSettings:
    """Load SardisSettings once per process to keep services consistent."""
    import os
    import logging

    logger = logging.getLogger(__name__)
    env_path = Path(env_file) if env_file else None
    settings = SardisSettings(_env_file=env_path)

    # Run production validation
    env = _normalize_environment(os.getenv("SARDIS_ENVIRONMENT", settings.environment))
    if env in ("prod", "sandbox"):
        errors = validate_production_config(settings)
        if errors:
            logger.warning("=" * 60)
            logger.warning("PRODUCTION CONFIGURATION WARNINGS")
            logger.warning("=" * 60)
            for error in errors:
                logger.warning(f"  - {error}")
            logger.warning("=" * 60)

            # In strict mode, raise error for critical missing config
            critical_errors = [e for e in errors if any(k in e for k in
                ["SECRET_KEY", "JWT_SECRET", "ADMIN_PASSWORD", "DATABASE_URL"])]
            if critical_errors and os.getenv("SARDIS_STRICT_CONFIG", "false").lower() == "true":
                raise RuntimeError(
                    f"Critical configuration missing: {', '.join(critical_errors)}. "
                    "Set SARDIS_STRICT_CONFIG=false to bypass (not recommended)."
                )

    return settings
