"""
S3 upload configuration models.

Defines configuration structure for S3 uploads following the
finalized requirements with global months and org-level overrides.
"""

import os
import yaml
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, Dict, Any, Union

from ...services.environment_service import EnvironmentService


@dataclass
class S3Config:
    """S3 configuration for uploads."""
    bucket: str
    region: str
    root_folder: str = "billing-data"
    
    @classmethod
    def from_environment(cls, upload_type: str = "adhoc") -> "S3Config":
        """Create S3 config from environment variables."""
        env_service = EnvironmentService()
        
        # Set root folder based on upload type
        if upload_type == "generated":
            root_folder = "piedpiper-custom-billing"
        else:
            root_folder = "billing-data"
        
        return cls(
            bucket=env_service.require("CZBILLGEN_S3_BUCKET"),
            region=env_service.get("AWS_DEFAULT_REGION", "us-east-1"),
            root_folder=root_folder
        )


@dataclass
class ProviderConfig:
    """Provider configuration with optional month overrides."""
    months: Optional[List[str]] = None


@dataclass
class Organization:
    """Organization configuration for multi-org upload."""
    name: str
    api_token_env: str
    providers: List[str]  # List of provider names
    months: Optional[List[str]] = None  # Optional month override
    stage_overrides: Optional[Dict[str, str]] = None  # Optional stage overrides per provider
    connections: Optional[Dict[str, List[str]]] = None  # CloudZero connection IDs


@dataclass
class AdhocUploadConfig:
    """Configuration for ad-hoc CSV upload operations."""
    csv_path: str
    mapping_path: Optional[str] = None  # Path to custom column mapping file


@dataclass 
class UploadConfig:
    """Complete upload configuration."""
    dataset_name: Optional[str] = None  # For generated uploads
    months: Optional[List[str]] = None  # Global months (smart default)
    providers: Optional[List[str]] = None  # Global providers (smart default)
    organizations: List[Organization] = None
    adhoc: Optional[AdhocUploadConfig] = None  # For ad-hoc uploads
    
    # Stage discovery is automatic - no stage_name needed
    
    @classmethod
    def from_yaml(cls, config_path: Path) -> "UploadConfig":
        """
        Load upload configuration from YAML file.
        
        Expected format (smart defaults structure):
        upload_config:
          dataset_name: "2025-01-15_14-30-22"
          months: ["2025-01", "2025-02"]  # Global default months
          providers: ["AWS", "GCP", "Azure"]  # Global default providers
          organizations:
            ORG_NAME:
              api_token_env: "CZ_ORG_API_KEY_ORGNAME"
              # months: ["2025-01"]  # Optional override
              # providers: ["AWS"]    # Optional override
              # stage_overrides:      # Optional stage overrides
              #   GCP: "usage"        # Force GCP to use usage stage
        """
        if not config_path.exists():
            raise FileNotFoundError(f"Upload config not found: {config_path}")
        
        with open(config_path, 'r') as f:
            raw_config = yaml.safe_load(f)
        
        # Extract main configuration section
        if "upload_config" not in raw_config:
            raise ValueError("Configuration must contain 'upload_config' section")
        
        config_data = raw_config["upload_config"]
        
        # Parse basic fields for generated uploads
        dataset_name = config_data.get("dataset_name")
        global_months = config_data.get("months")
        global_providers = config_data.get("providers")
        
        # Parse ad-hoc configuration if present
        adhoc_config = None
        if "adhoc" in config_data:
            adhoc_data = config_data["adhoc"]
            adhoc_config = AdhocUploadConfig(
                csv_path=adhoc_data["csv_path"],
                mapping_path=adhoc_data.get("mapping_path")
            )
        
        # Parse smart defaults structure: organizations dict with inheritance
        organizations = cls._parse_smart_defaults_structure(config_data, global_months, global_providers)
        
        # Validate required fields
        cls._validate_config(dataset_name, organizations, adhoc_config)
        
        return cls(
            dataset_name=dataset_name,
            months=global_months,
            providers=global_providers,
            organizations=organizations,
            adhoc=adhoc_config
        )
    
    @classmethod
    def _parse_smart_defaults_structure(cls, config_data: Dict[str, Any], global_months: Optional[List[str]], global_providers: Optional[List[str]]) -> List[Organization]:
        """
        Parse smart defaults configuration structure with global inheritance.
        
        Organizations inherit global months/providers unless overridden.
        
        Args:
            config_data: Parsed YAML configuration data
            global_months: Global default months (can be None)
            global_providers: Global default providers (can be None)
            
        Returns:
            List of Organization objects with smart defaults applied
        """
        # Parse organizations definitions
        orgs_definitions = config_data.get("organizations", {})
        if not isinstance(orgs_definitions, dict):
            raise ValueError("'organizations' must be a dictionary mapping org names to definitions")
        
        if not orgs_definitions:
            raise ValueError("At least one organization must be defined")
        
        # Build organizations list with smart defaults
        organizations = []
        env_service = EnvironmentService()
        
        for org_name, org_def in orgs_definitions.items():
            if not isinstance(org_def, dict):
                raise ValueError(f"Organization definition for '{org_name}' must be a dictionary")
            
            # Parse API token environment variable (required)
            api_token_env = org_def.get("api_token_env")
            if not api_token_env:
                raise ValueError(f"Organization '{org_name}' missing required 'api_token_env' field")
            
            # Parse providers with inheritance from global defaults
            org_providers = org_def.get("providers")
            if org_providers is not None:
                if not isinstance(org_providers, list):
                    raise ValueError(f"Organization '{org_name}' providers must be a list of provider names")
                final_providers = org_providers
            else:
                # Inherit from global providers
                if global_providers is None:
                    raise ValueError(f"Organization '{org_name}' has no providers specified and no global providers defined")
                final_providers = global_providers.copy()
            
            # Parse months with inheritance from global defaults
            org_months = org_def.get("months")
            if org_months is not None:
                if not isinstance(org_months, list):
                    raise ValueError(f"Organization '{org_name}' months must be a list of month strings")
                final_months = org_months
            else:
                # Inherit from global months (can be None - will be resolved later)
                final_months = global_months.copy() if global_months else None
            
            # Parse stage overrides (optional)
            stage_overrides = org_def.get("stage_overrides")
            if stage_overrides is not None:
                if not isinstance(stage_overrides, dict):
                    raise ValueError(f"Organization '{org_name}' stage_overrides must be a dictionary mapping provider to stage")
                
                # Validate override providers are in the provider list
                for override_provider in stage_overrides.keys():
                    if override_provider not in final_providers:
                        raise ValueError(f"Organization '{org_name}' stage override for '{override_provider}' but provider not in providers list")
            
            # Create Organization object
            org = Organization(
                name=org_name,
                api_token_env=api_token_env,
                providers=final_providers,
                months=final_months,
                stage_overrides=stage_overrides,
                connections=org_def.get("connections", {})
            )
            organizations.append(org)
        
        return organizations
    
    
    @staticmethod
    def _validate_config(
        dataset_name: Optional[str],
        organizations: List[Organization],
        adhoc_config: Optional[AdhocUploadConfig]
    ) -> None:
        """Validate required configuration fields."""
        # Either generated or ad-hoc configuration must be provided
        is_generated = dataset_name is not None
        is_adhoc = adhoc_config is not None
        
        if not is_generated and not is_adhoc:
            raise ValueError("Either dataset_name (for generated uploads) or adhoc configuration must be provided")
        
        if is_generated and is_adhoc:
            raise ValueError("Cannot specify both generated (dataset_name) and ad-hoc configuration")
        
        # Validate generated upload configuration
        if is_generated:
            if not dataset_name:
                raise ValueError("Dataset name is required for generated uploads")
        
        # Validate ad-hoc upload configuration
        if is_adhoc:
            if not adhoc_config.csv_path:
                raise ValueError("CSV path is required for ad-hoc uploads")
            
            csv_path = Path(adhoc_config.csv_path)
            if not csv_path.exists():
                raise ValueError(f"CSV file not found: {csv_path}")
            
            if adhoc_config.mapping_path:
                mapping_path = Path(adhoc_config.mapping_path)
                if not mapping_path.exists():
                    raise ValueError(f"Mapping file not found: {mapping_path}")
        
        # Organizations are required for both types
        if not organizations:
            raise ValueError("At least one organization is required")
        
        # Validate each organization
        for org in organizations:
            if not org.name:
                raise ValueError("Organization name is required")
            if not org.api_token_env:
                raise ValueError(f"API token environment variable is required for organization {org.name}")
            if not org.providers:
                raise ValueError(f"Providers list is required for organization {org.name}")
            
            # Validate provider names
            for provider in org.providers:
                if not isinstance(provider, str) or not provider.strip():
                    raise ValueError(f"Organization {org.name} contains invalid provider name: '{provider}'")
            
            # Validate months format if specified
            if org.months:
                for month in org.months:
                    if not isinstance(month, str) or not month.strip():
                        raise ValueError(f"Organization {org.name} contains invalid month: '{month}'")
            
            # Validate stage overrides if specified
            if org.stage_overrides:
                valid_stages = ["discount", "usage", "metadata", "k8s", "generate"]
                for provider, stage in org.stage_overrides.items():
                    if stage not in valid_stages:
                        raise ValueError(f"Organization {org.name} stage override '{stage}' for provider '{provider}' is invalid. Valid stages: {valid_stages}")
            
            # Validate environment variable exists
            env_service = EnvironmentService()
            try:
                env_service.require(org.api_token_env)
            except ValueError:
                raise ValueError(f"Environment variable {org.api_token_env} not found for organization {org.name}")
    
    
    def is_adhoc_upload(self) -> bool:
        """Check if this is an ad-hoc upload configuration."""
        return self.adhoc is not None
    
    def is_generated_upload(self) -> bool:
        """Check if this is a generated dataset upload configuration."""
        return self.dataset_name is not None


def create_example_config() -> Dict[str, Any]:
    """Create example upload configuration using smart defaults structure."""
    return {
        "upload_config": {
            "dataset_name": "2025-01-15_14-30-22",
            "months": ["2025-01", "2025-02", "2025-03"],  # Global default months
            "providers": ["AWS", "GCP", "Azure"],  # Global default providers
            "organizations": {
                "TradeShow": {
                    "api_token_env": "CZ_ORG_API_KEY_TRADESHOW"
                    # Inherits global months + providers
                },
                "Demo": {
                    "api_token_env": "CZ_ORG_API_KEY_DEMO",
                    "providers": ["AWS", "GCP"],  # Override: only AWS + GCP
                    "stage_overrides": {  # Optional: force specific stages
                        "GCP": "usage"  # Force GCP to use usage stage
                    }
                }
            }
        }
    }