"""
Environment Service for centralized ~/.czbg/.env management.

Provides a single point for loading and accessing environment variables
from the standard ~/.czbg/.env file used throughout the codebase.
"""

import os
from pathlib import Path
from typing import Optional

from dotenv import load_dotenv

from ..log_context import get_logger


class EnvironmentService:
    """
    Centralized service for ~/.czbg/.env environment variable management.
    
    Handles loading, caching, and accessing environment variables from the
    standard czbillgen environment file location.
    """
    
    _instance: Optional['EnvironmentService'] = None
    _loaded: bool = False
    
    def __new__(cls) -> 'EnvironmentService':
        """Singleton pattern to ensure environment is loaded only once."""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        """Initialize environment service and load variables if not already loaded."""
        if not self._loaded:
            self.logger = get_logger(__name__)
            self._load_environment()
            EnvironmentService._loaded = True
    
    def _load_environment(self) -> None:
        """Load environment variables from ~/.czbg/.env file."""
        env_file = Path.home() / ".czbg" / ".env"
        
        if env_file.exists():
            load_dotenv(env_file, override=True)
            self.logger.debug(f"Loaded environment variables from {env_file}")
        else:
            self.logger.debug(f"Environment file not found at {env_file}")
    
    def get(self, key: str, default: Optional[str] = None) -> Optional[str]:
        """
        Get environment variable value.
        
        Args:
            key: Environment variable name
            default: Default value if not found
            
        Returns:
            Environment variable value or default
        """
        return os.getenv(key, default)
    
    def require(self, key: str, error_message: Optional[str] = None) -> str:
        """
        Get required environment variable, raising error if not found.
        
        Args:
            key: Environment variable name
            error_message: Custom error message if not found
            
        Returns:
            Environment variable value
            
        Raises:
            ValueError: If environment variable is not set
        """
        value = os.getenv(key)
        if value is None:
            if error_message:
                raise ValueError(error_message)
            else:
                raise ValueError(
                    f"{key} not configured. "
                    f"Add it to your ~/.czbg/.env file."
                )
        return value
    
    def is_configured(self, key: str) -> bool:
        """
        Check if environment variable is configured.
        
        Args:
            key: Environment variable name
            
        Returns:
            True if environment variable is set and non-empty
        """
        value = os.getenv(key)
        return value is not None and value.strip() != ""
    
    def get_s3_config(self) -> dict[str, str]:
        """
        Get S3-related configuration from environment variables.
        
        Returns:
            Dictionary with S3 configuration
            
        Raises:
            ValueError: If required S3 configuration is missing
        """
        return {
            "bucket": self.require(
                "CZBILLGEN_S3_BUCKET",
                "CZBILLGEN_S3_BUCKET not configured. Add it to your ~/.czbg/.env file."
            ),
            "reference_path": self.get("CZBILLGEN_S3_REFERENCE_PATH", "czbillgen/reference_data"),
            "region": self.get("AWS_DEFAULT_REGION", "us-east-1"),
            "profile": self.get("AWS_PROFILE"),  # Optional AWS profile name
        }
    
    def get_aws_credentials(self) -> dict[str, Optional[str]]:
        """
        Get AWS credentials using boto3 default credential chain.
        
        This method supports AWS SSO, environment variables, IAM roles, and other
        standard AWS credential sources automatically through boto3.
        
        Returns:
            Empty dictionary - boto3 will handle credential discovery automatically
            
        Note:
            This method now returns an empty dict as boto3 clients should be created
            without explicit credentials to use the default credential chain.
        """
        # Return empty dict - boto3 will use default credential chain
        # This includes: SSO, environment variables, IAM roles, etc.
        return {}
    
    def validate_aws_credentials_and_access(self) -> None:
        """
        Validate AWS credentials and basic S3 access for fail-fast behavior.
        
        This method performs lightweight validation to ensure AWS credentials
        are available and can access the configured S3 bucket before any 
        S3-backed operations begin.
        
        Raises:
            NoCredentialsError: If AWS credentials are not found
            ClientError: If cannot access S3 bucket or other AWS errors
        """
        import boto3
        from botocore.exceptions import NoCredentialsError, ClientError
        
        # Get S3 configuration
        s3_config = self.get_s3_config()
        bucket = s3_config["bucket"]
        region = s3_config["region"]
        profile = s3_config["profile"]
        
        # Create session with profile if specified
        if profile:
            session = boto3.Session(profile_name=profile)
            self.logger.debug(f"Validating AWS credentials with profile '{profile}'")
        else:
            session = boto3.Session()
            self.logger.debug("Validating AWS credentials with default profile")
        
        # Quick credential check + basic S3 access test
        s3_client = session.client('s3', region_name=region)
        s3_client.head_bucket(Bucket=bucket)
        
        self.logger.debug("AWS credentials and S3 access validated successfully")
    
    def get_generation_config(self) -> dict[str, any]:
        """
        Get generation performance configuration from environment variables.
        
        Returns:
            Dictionary with generation configuration
        """
        return {
            "accumulation_memory_mb": int(self.get("CZBILLGEN_ACCUMULATION_MEMORY_MB", "500")),
            "memory_log_level": self.get("CZBILLGEN_MEMORY_LOG_LEVEL", "INFO").upper(),
        }
    
    def validate_s3_configuration(self) -> None:
        """
        Validate that all required S3 configuration is present.
        
        Raises:
            ValueError: If any required S3 configuration is missing
        """
        # This will raise ValueError if any required config is missing
        self.get_s3_config()
        self.get_aws_credentials()


# Global instance for easy access
env_service = EnvironmentService()