"""
Credo SDK Configuration

Manages environment settings, API keys, and base URLs.
Configuration can be set via Django settings or environment variables using python-decouple.
"""

from dataclasses import dataclass, field
from typing import List
from enum import Enum


class Environment(Enum):
    """Credo environment options."""
    SANDBOX = "sandbox"
    PRODUCTION = "production"


@dataclass
class CredoConfig:
    """
    Configuration for Credo SDK.
    
    Attributes:
        public_key: Your Credo public API key (from dashboard)
        secret_key: Your Credo secret API key (from dashboard)
        environment: 'sandbox' or 'production'
        webhook_token: Token for webhook signature verification (you set this in Credo dashboard)
        business_code: Your Credo business code (auto-generated by Credo, get from dashboard Settings)
        terminal_id: Your Credo terminal ID
        default_currency: Default currency for transactions (default: NGN)
        default_callback_url: Default callback URL for payments
        default_channels: Default payment channels
        logo_url: Logo URL for payment page (60x60px)
    
    Usage:
        # Option 1: Initialize with parameters
        config = CredoConfig(
            public_key="your-public-key",
            secret_key="your-secret-key",
            environment="sandbox"
        )
        
        # Option 2: Load from Django settings (recommended)
        config = CredoConfig.from_django_settings()
        
        # Option 3: Load from environment variables using decouple
        config = CredoConfig.from_env()
    """
    
    public_key: str = ""
    secret_key: str = ""
    environment: str = "sandbox"
    webhook_token: str = ""
    # Business code is auto-generated by Credo - retrieve from dashboard Settings
    # Required for webhook signature verification: SHA512(webhook_token + business_code)
    business_code: str = ""
    terminal_id: str = "0000000001"  # Default sandbox terminal ID
    default_currency: str = "NGN"
    default_callback_url: str = ""
    default_channels: List[str] = field(default_factory=lambda: ["card", "bank"])
    logo_url: str = ""  # Must be 60x60px per Credo docs
    default_bearer: int = 0  # 0 = customer pays fee, 1 = merchant pays fee
    
    # API Base URLs (per Credo documentation)
    SANDBOX_BASE_URL: str = field(default="https://api.credodemo.com", init=False)
    PRODUCTION_BASE_URL: str = field(default="https://api.credocentral.com", init=False)
    
    # Inline Payment Script URLs
    SANDBOX_INLINE_URL: str = field(default="https://pay.credodemo.com/inline.js", init=False)
    PRODUCTION_INLINE_URL: str = field(default="https://pay.credocentral.com/inline.js", init=False)
    
    # Dashboard URLs (for reference)
    SANDBOX_DASHBOARD_URL: str = field(default="https://www.credodemo.com", init=False)
    PRODUCTION_DASHBOARD_URL: str = field(default="https://www.credocentral.com", init=False)
    
    def __post_init__(self):
        """Validate configuration after initialization."""
        self.environment = self.environment.lower()
        if self.environment not in ["sandbox", "production"]:
            raise ValueError(f"Invalid environment: {self.environment}. Must be 'sandbox' or 'production'.")
    
    @property
    def base_url(self) -> str:
        """Get the base URL based on environment."""
        if self.environment == "production":
            return self.PRODUCTION_BASE_URL
        return self.SANDBOX_BASE_URL
    
    @property
    def inline_script_url(self) -> str:
        """Get the inline payment script URL based on environment."""
        if self.environment == "production":
            return self.PRODUCTION_INLINE_URL
        return self.SANDBOX_INLINE_URL
    
    @property
    def dashboard_url(self) -> str:
        """Get the dashboard URL based on environment."""
        if self.environment == "production":
            return self.PRODUCTION_DASHBOARD_URL
        return self.SANDBOX_DASHBOARD_URL
    
    @property
    def is_production(self) -> bool:
        """Check if running in production environment."""
        return self.environment == "production"
    
    @classmethod
    def from_django_settings(cls) -> "CredoConfig":
        """
        Load configuration from Django settings.
        
        This is the recommended approach as Django settings can use
        python-decouple for clean environment variable handling.
        
        Expected Django settings:
            CREDO_PUBLIC_KEY = config('CREDO_PUBLIC_KEY', default='')
            CREDO_SECRET_KEY = config('CREDO_SECRET_KEY', default='')
            CREDO_ENVIRONMENT = config('CREDO_ENVIRONMENT', default='sandbox')
            CREDO_WEBHOOK_SECRET_TOKEN = config('CREDO_WEBHOOK_SECRET_TOKEN', default='')
            CREDO_BUSINESS_CODE = config('CREDO_BUSINESS_CODE', default='')
            CREDO_TERMINAL_ID = config('CREDO_TERMINAL_ID', default='0000000001')
            CREDO_DEFAULT_CURRENCY = config('CREDO_DEFAULT_CURRENCY', default='NGN')
            CREDO_DEFAULT_CALLBACK_URL = config('CREDO_DEFAULT_CALLBACK_URL', default='')
            CREDO_DEFAULT_CHANNELS = config('CREDO_DEFAULT_CHANNELS', default='card,bank', cast=Csv())
            CREDO_LOGO_URL = config('CREDO_LOGO_URL', default='')
            CREDO_DEFAULT_BEARER = config('CREDO_DEFAULT_BEARER', default=0, cast=int)
        """
        try:
            from django.conf import settings
        except ImportError:
            raise ImportError("Django is required to use from_django_settings()")
        
        # Handle channels - could be list or comma-separated string
        channels = getattr(settings, "CREDO_DEFAULT_CHANNELS", ["card", "bank"])
        if isinstance(channels, str):
            channels = [c.strip() for c in channels.split(",")]
        
        return cls(
            public_key=getattr(settings, "CREDO_PUBLIC_KEY", ""),
            secret_key=getattr(settings, "CREDO_SECRET_KEY", ""),
            environment=getattr(settings, "CREDO_ENVIRONMENT", "sandbox"),
            webhook_token=getattr(settings, "CREDO_WEBHOOK_SECRET_TOKEN", ""),
            business_code=getattr(settings, "CREDO_BUSINESS_CODE", ""),
            terminal_id=getattr(settings, "CREDO_TERMINAL_ID", "0000000001"),
            default_currency=getattr(settings, "CREDO_DEFAULT_CURRENCY", "NGN"),
            default_callback_url=getattr(settings, "CREDO_DEFAULT_CALLBACK_URL", ""),
            default_channels=list(channels),
            logo_url=getattr(settings, "CREDO_LOGO_URL", ""),
            default_bearer=getattr(settings, "CREDO_DEFAULT_BEARER", 0),
        )
    
    @classmethod
    def from_env(cls) -> "CredoConfig":
        """
        Load configuration from environment variables using python-decouple.
        
        This method uses python-decouple for cleaner env variable handling.
        
        Expected environment variables (in .env file):
            CREDO_PUBLIC_KEY=your_public_key
            CREDO_SECRET_KEY=your_secret_key
            CREDO_ENVIRONMENT=sandbox
            CREDO_WEBHOOK_SECRET_TOKEN=your_webhook_token
            CREDO_BUSINESS_CODE=your_business_code
            CREDO_TERMINAL_ID=0000000001
            CREDO_DEFAULT_CURRENCY=NGN
            CREDO_DEFAULT_CALLBACK_URL=http://localhost:8000/callback/
            CREDO_DEFAULT_CHANNELS=card,bank
            CREDO_LOGO_URL=
            CREDO_DEFAULT_BEARER=0
        """
        try:
            from decouple import config, Csv
        except ImportError:
            raise ImportError(
                "python-decouple is required for from_env(). "
                "Install with: pip install python-decouple"
            )
        
        return cls(
            public_key=config("CREDO_PUBLIC_KEY", default=""),
            secret_key=config("CREDO_SECRET_KEY", default=""),
            environment=config("CREDO_ENVIRONMENT", default="sandbox"),
            webhook_token=config("CREDO_WEBHOOK_SECRET_TOKEN", default=""),
            business_code=config("CREDO_BUSINESS_CODE", default=""),
            terminal_id=config("CREDO_TERMINAL_ID", default="0000000001"),
            default_currency=config("CREDO_DEFAULT_CURRENCY", default="NGN"),
            default_callback_url=config("CREDO_DEFAULT_CALLBACK_URL", default=""),
            default_channels=config("CREDO_DEFAULT_CHANNELS", default="card,bank", cast=Csv()),
            logo_url=config("CREDO_LOGO_URL", default=""),
            default_bearer=config("CREDO_DEFAULT_BEARER", default=0, cast=int),
        )
    
    def validate(self) -> bool:
        """
        Validate that required configuration is present.
        
        Returns:
            bool: True if configuration is valid
            
        Raises:
            CredoConfigError: If required configuration is missing
        """
        from .exceptions import CredoConfigError
        
        errors = []
        warnings = []
        
        # Required for all environments
        if not self.public_key:
            errors.append("CREDO_PUBLIC_KEY is required")
        if not self.secret_key:
            errors.append("CREDO_SECRET_KEY is required")
        
        # Production-specific requirements
        if self.is_production:
            if self.terminal_id == "0000000001":
                errors.append(
                    "Production requires a valid CREDO_TERMINAL_ID "
                    "(not the sandbox default '0000000001'). "
                    "Request from your Credo account officer."
                )
        
        # Webhook verification requirements (needed for both environments if using webhooks)
        if self.webhook_token and not self.business_code:
            warnings.append(
                "CREDO_BUSINESS_CODE is recommended when using webhooks. "
                "Get it from your Credo dashboard > Settings. "
                "Required for signature verification: SHA512(token + business_code)"
            )
        
        if errors:
            raise CredoConfigError(f"Configuration errors: {'; '.join(errors)}")
        
        return True
    
    def get_config_summary(self) -> dict:
        """
        Get a summary of the current configuration (for debugging).
        
        Returns:
            dict with configuration details (secrets masked)
        """
        return {
            "environment": self.environment,
            "base_url": self.base_url,
            "public_key": f"{self.public_key[:10]}..." if self.public_key else "NOT SET",
            "secret_key": "***SET***" if self.secret_key else "NOT SET",
            "terminal_id": self.terminal_id,
            "business_code": f"{self.business_code[:6]}..." if self.business_code else "NOT SET",
            "webhook_token": "***SET***" if self.webhook_token else "NOT SET",
            "default_currency": self.default_currency,
            "default_channels": self.default_channels,
            "default_bearer": "Customer" if self.default_bearer == 0 else "Merchant",
            "default_callback_url": self.default_callback_url or "NOT SET",
            "logo_url": self.logo_url or "NOT SET",
        }
