"""Configuration management for AI agents.

This module provides centralized configuration using Pydantic settings,
supporting environment variables, .env files, and AWS SSM Parameter Store.

Usage:
    from agent_factory.core.config import BaseAgentConfig, get_ssm_parameter

    class MyAgentConfig(BaseAgentConfig):
        custom_setting: str = Field(default="value")

    config = MyAgentConfig()
"""

import os
from typing import Any, Optional

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class BaseAgentConfig(BaseSettings):
    """Base configuration for AI agents.

    Inherit from this class to create agent-specific configurations.
    All settings can be overridden by environment variables.

    Example:
        class BertaConfig(BaseAgentConfig):
            quickbooks_client_id: str = Field(default="")
            allowed_channels: list[str] = Field(default_factory=list)
    """

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="ignore",
    )

    # Environment
    environment: str = Field(default="dev", description="Environment name (dev, stg, prod)")
    log_level: str = Field(default="INFO", description="Logging level")
    debug: bool = Field(default=False, description="Enable debug mode")

    # AWS Configuration
    aws_region: str = Field(default="us-east-1", alias="AWS_DEFAULT_REGION")

    # AI Model Configuration
    bedrock_model_id: str = Field(
        default="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
        description="AWS Bedrock model ID (cross-region inference profile)",
    )
    anthropic_model_id: str = Field(
        default="claude-3-5-sonnet-20241022",
        description="Anthropic model ID for fallback",
    )
    max_tokens: int = Field(default=4096, description="Maximum tokens for AI responses")

    # Circuit Breaker
    circuit_breaker_failure_threshold: int = Field(
        default=5, description="Number of failures before circuit breaker opens"
    )
    circuit_breaker_recovery_timeout: int = Field(
        default=60, description="Seconds before circuit breaker tests recovery"
    )
    circuit_breaker_success_threshold: int = Field(
        default=2, description="Successes needed to close circuit in half-open state"
    )

    # Observability
    service_name: str = Field(default="agent", description="Service name for logging/metrics")
    metrics_namespace: str = Field(
        default="AgentFactory", description="CloudWatch metrics namespace"
    )

    @property
    def is_production(self) -> bool:
        """Check if running in production environment."""
        return self.environment == "prod"

    @property
    def is_development(self) -> bool:
        """Check if running in development environment."""
        return self.environment == "dev"


# SSM Parameter Store Integration
_ssm_client = None
_ssm_cache: dict[str, Any] = {}


def get_ssm_client():
    """Get or create SSM client."""
    global _ssm_client
    if _ssm_client is None:
        import boto3

        _ssm_client = boto3.client("ssm")
    return _ssm_client


def get_ssm_parameter(
    name: str,
    *,
    decrypt: bool = True,
    use_cache: bool = True,
) -> Optional[str]:
    """Get a parameter from AWS SSM Parameter Store.

    Args:
        name: Parameter name or full path
        decrypt: Whether to decrypt SecureString parameters
        use_cache: Whether to use cached values

    Returns:
        Parameter value or None if not found

    Example:
        bot_token = get_ssm_parameter("/myagent/slack/bot-token")
    """
    if use_cache and name in _ssm_cache:
        return _ssm_cache[name]

    try:
        client = get_ssm_client()
        response = client.get_parameter(Name=name, WithDecryption=decrypt)
        value = response["Parameter"]["Value"]

        if use_cache:
            _ssm_cache[name] = value

        return value

    except Exception:
        return None


def get_ssm_parameters(
    names: list[str],
    *,
    decrypt: bool = True,
    use_cache: bool = True,
) -> dict[str, Optional[str]]:
    """Get multiple parameters from SSM Parameter Store.

    Args:
        names: List of parameter names
        decrypt: Whether to decrypt SecureString parameters
        use_cache: Whether to use cached values

    Returns:
        Dictionary mapping parameter names to values

    Example:
        params = get_ssm_parameters([
            "/myagent/slack/bot-token",
            "/myagent/slack/signing-secret",
        ])
    """
    result: dict[str, Optional[str]] = {}

    # Check cache first
    names_to_fetch = []
    for name in names:
        if use_cache and name in _ssm_cache:
            result[name] = _ssm_cache[name]
        else:
            names_to_fetch.append(name)

    if not names_to_fetch:
        return result

    try:
        client = get_ssm_client()

        # SSM allows max 10 parameters per request
        for i in range(0, len(names_to_fetch), 10):
            batch = names_to_fetch[i : i + 10]
            response = client.get_parameters(Names=batch, WithDecryption=decrypt)

            for param in response.get("Parameters", []):
                name = param["Name"]
                value = param["Value"]
                result[name] = value
                if use_cache:
                    _ssm_cache[name] = value

            # Mark missing parameters as None
            for name in response.get("InvalidParameters", []):
                result[name] = None

    except Exception:
        # Return None for all unfetched parameters
        for name in names_to_fetch:
            if name not in result:
                result[name] = None

    return result


def clear_ssm_cache() -> None:
    """Clear the SSM parameter cache."""
    global _ssm_cache
    _ssm_cache = {}


def get_env_or_ssm(
    env_var: str,
    ssm_param: Optional[str] = None,
    default: Optional[str] = None,
) -> Optional[str]:
    """Get a value from environment variable or SSM.

    First checks for an environment variable, then falls back to SSM.

    Args:
        env_var: Environment variable name
        ssm_param: SSM parameter name (optional)
        default: Default value if not found

    Returns:
        Value from env, SSM, or default

    Example:
        # Lambda env var points to SSM parameter name
        ssm_param_name = os.getenv("SSM_SLACK_BOT_TOKEN")
        bot_token = get_env_or_ssm(
            "SLACK_BOT_TOKEN",
            ssm_param=ssm_param_name,
        )
    """
    # Check direct environment variable first
    value = os.getenv(env_var)
    if value:
        return value

    # Check SSM if parameter name provided
    if ssm_param:
        value = get_ssm_parameter(ssm_param)
        if value:
            return value

    return default
