"""
Upload validation service implementation.

Provides validation operations for upload workflows including S3 access,
stage discovery coordination, and connection validation.
"""

from typing import Dict, List
from ..interfaces import ValidationServiceInterface
from ..models import (
    ValidationResult,
    StageDiscoveryResult, 
    ConnectionValidationResult,
    ProcessingPlan,
    OrganizationPlan,
    ProviderPlan,
    UploadConfig,
    ValidationStatus
)
from ..s3.uploader import S3UploadError
from ..s3.stage_discovery import StageDiscoveryService, StageDiscoveryError, PartialDataError, NoDataError


class UploadValidationService(ValidationServiceInterface):
    """
    Implementation of upload validation operations.
    
    Coordinates S3 validation, stage discovery, and connection validation
    for upload workflows following Single Responsibility Principle.
    """
    
    def __init__(self, s3_uploader, stage_discovery_service: StageDiscoveryService):
        """
        Initialize validation service with dependencies.
        
        Args:
            s3_uploader: S3 uploader for bucket validation
            stage_discovery_service: Service for stage discovery operations
        """
        self.s3_uploader = s3_uploader
        self.stage_discovery = stage_discovery_service
    
    def validate_s3_access(self, bucket: str, region: str) -> ValidationResult:
        """
        Validate S3 bucket access and permissions.
        
        Args:
            bucket: S3 bucket name
            region: S3 region
            
        Returns:
            ValidationResult with S3 access validation outcome
        """
        try:
            self.s3_uploader.validate_bucket_access()
            print("✅ S3 bucket access validated")
            return ValidationResult(
                status=ValidationStatus.SUCCESS,
                validation_step="s3_bucket"
            )
        except S3UploadError as e:
            return ValidationResult(
                status=ValidationStatus.FAILED,
                error_message=f"S3 bucket validation failed: {str(e)}",
                validation_step="s3_bucket"
            )
    
    def discover_stages_for_organizations(
        self,
        upload_config: UploadConfig
    ) -> Dict[str, List[StageDiscoveryResult]]:
        """
        Perform stage discovery for all organizations.
        
        Args:
            upload_config: Upload configuration with dataset and org details
            
        Returns:
            Dict mapping organization name to list of StageDiscoveryResult
        """
        org_discovery_results = {}
        
        for org in upload_config.organizations:
            # Resolve months for this organization (org-level override or global default)
            org_months = org.months if org.months else upload_config.months
            if not org_months:
                raise ValueError(f"Organization {org.name} has no months specified and no global months defined")
            
            # Perform discovery for this organization's providers
            discovery_results = self.stage_discovery.discover_provider_stages(
                dataset_name=upload_config.dataset_name,
                providers=org.providers,
                required_months=org_months,
                stage_overrides=org.stage_overrides
            )
            
            org_discovery_results[org.name] = discovery_results
        
        return org_discovery_results
    
    def validate_connections_for_organizations(
        self,
        upload_config: UploadConfig
    ) -> Dict[str, ConnectionValidationResult]:
        """
        Validate connections for all organizations.
        
        Args:
            upload_config: Upload configuration with organization details
            
        Returns:
            Dict mapping organization name to ConnectionValidationResult
        """
        org_validation_results = {}
        
        for org in upload_config.organizations:
            try:
                providers = org.providers  # Now a simple list
                
                if providers:
                    validation_result = self._validate_organization_connections(
                        org, providers, upload_config
                    )
                    org_validation_results[org.name] = validation_result
                else:
                    org_validation_results[org.name] = ConnectionValidationResult(
                        organization=org.name,
                        providers=[],
                        status=ValidationStatus.SUCCESS  # No providers = no validation needed
                    )
                    
            except Exception as e:
                org_validation_results[org.name] = ConnectionValidationResult(
                    organization=org.name,
                    providers=providers if 'providers' in locals() else [],
                    status=ValidationStatus.FAILED,
                    error_message=str(e)
                )
        
        return org_validation_results
    
    def generate_processing_plan(
        self,
        connection_results: Dict[str, ConnectionValidationResult],
        discovery_results: Dict[str, List[StageDiscoveryResult]],
        is_dry_run: bool = False
    ) -> ProcessingPlan:
        """
        Generate processing plan from validation results.
        
        Args:
            connection_results: Connection validation results per organization
            discovery_results: Stage discovery results per organization
            is_dry_run: Whether this is a dry run validation
            
        Returns:
            ProcessingPlan with structured plan for upload execution
        """
        organizations = []
        total_files = 0
        
        for org_name, conn_result in connection_results.items():
            discovery_list = discovery_results.get(org_name, [])
            
            if conn_result.status != ValidationStatus.SUCCESS:
                # Skip organizations that failed connection validation
                organizations.append(OrganizationPlan(
                    name=org_name,
                    status=conn_result.status.value,
                    providers=[],
                    error=conn_result.error_message
                ))
                continue
            
            # Create provider plans from discovery results
            provider_plans = []
            for discovery_result in discovery_list:
                provider_plan = ProviderPlan(
                    name=discovery_result.provider,
                    stage=discovery_result.stage,
                    months=discovery_result.available_months,
                    file_count=len(discovery_result.available_months),
                    discovery_method=discovery_result.discovery_method
                )
                provider_plans.append(provider_plan)
                total_files += len(discovery_result.available_months)
            
            organizations.append(OrganizationPlan(
                name=org_name,
                status="ready",
                providers=provider_plans
            ))
        
        return ProcessingPlan(
            organizations=organizations,
            total_files=total_files,
            total_organizations=len(connection_results)
        )
    
    def perform_unified_validation(self, upload_config: UploadConfig, is_dry_run: bool = False) -> ValidationResult:
        """
        Perform complete validation workflow.
        
        Coordinates S3 validation, stage discovery, connection validation,
        and processing plan generation in a single operation.
        
        Args:
            upload_config: Upload configuration
            is_dry_run: Whether this is a dry run validation
            
        Returns:
            ValidationResult with complete validation outcome and processing plan
        """
        # Step 1: Validate S3 bucket access
        s3_validation = self.validate_s3_access(upload_config.s3_bucket, upload_config.s3_region)
        if not s3_validation.is_successful():
            return s3_validation
        
        # Step 2: Perform stage discovery for all organizations
        try:
            discovery_results = self.discover_stages_for_organizations(upload_config)
            self._display_stage_discovery_results(discovery_results, upload_config.organizations)
            
            total_files = sum(
                len(result.available_months) 
                for results in discovery_results.values() 
                for result in results
            )
            print(f"✅ Stage discovery complete. Processing plan: {total_files} files across {len(upload_config.organizations)} organizations")
            
        except (StageDiscoveryError, PartialDataError, NoDataError) as e:
            return ValidationResult(
                status=ValidationStatus.FAILED,
                error_message=f"Stage discovery failed: {str(e)}",
                validation_step="stage_discovery"
            )
        except Exception as e:
            return ValidationResult(
                status=ValidationStatus.FAILED,
                error_message=f"Unexpected error during stage discovery: {str(e)}",
                validation_step="stage_discovery"
            )
        
        # Step 3: Validate connections for all organizations
        connection_results = self.validate_connections_for_organizations(upload_config)
        
        # Step 4: Generate processing plan
        processing_plan = self.generate_processing_plan(connection_results, discovery_results, is_dry_run)
        
        return ValidationResult(
            status=ValidationStatus.SUCCESS,
            validation_step="complete",
            details={
                "dataset_name": upload_config.dataset_name,
                "connection_results": connection_results,
                "processing_plan": processing_plan,
                "discovery_results": discovery_results
            }
        )
    
    def _validate_organization_connections(
        self,
        org,
        providers: List[str],
        upload_config: UploadConfig
    ) -> ConnectionValidationResult:
        """
        Validate connections for a single organization.

        Args:
            org: Organization configuration
            providers: List of provider names
            upload_config: Upload configuration

        Returns:
            ConnectionValidationResult for the organization
        """
        try:
            # Get organization API key
            from ..api.connection_manager import get_org_api_key, ConnectionManager
            org_api_key = get_org_api_key(org.name)
            conn_manager = ConnectionManager(org_api_key)

            print(f"   🔍 Ensuring connections for {len(providers)} providers...")

            # First check existing connections (with suffix if provided)
            conn_status = conn_manager.validate_connections_exist(
                providers, upload_config.connection_suffix
            )
            missing_providers = [p for p, exists in conn_status.items() if not exists]

            if missing_providers:
                print(f"   ⚙️  Auto-creating {len(missing_providers)} missing connections...")
                
                # Prepare S3 config for connection creation
                s3_config_dict = {
                    "bucket_name": upload_config.s3_bucket,
                    "root_folder": "piedpiper-custom-billing",  # Generated uploads use this folder
                    "org_folder": org.name  # Use organization name as folder
                }

                # Auto-create missing connections
                try:
                    conn_results = conn_manager.ensure_all_connections(
                        providers,
                        s3_config_dict,
                        connection_suffix=upload_config.connection_suffix
                    )

                    print(f"   ✅ {org.name}: All connections ready ({len(providers)} providers)")
                    return ConnectionValidationResult(
                        organization=org.name,
                        providers=providers,
                        status=ValidationStatus.SUCCESS,
                        auto_created_connections=missing_providers
                    )
                except Exception as auto_create_error:
                    print(f"   ❌ Failed to auto-create connections: {auto_create_error}")
                    return ConnectionValidationResult(
                        organization=org.name,
                        providers=providers,
                        status=ValidationStatus.FAILED,
                        missing_providers=missing_providers,
                        error_message=f"Failed to create connections: {str(auto_create_error)}"
                    )
            else:
                print(f"   ✅ {org.name}: All connections validated ({len(providers)} providers)")
                return ConnectionValidationResult(
                    organization=org.name,
                    providers=providers,
                    status=ValidationStatus.SUCCESS
                )
                
        except Exception as e:
            print(f"   ❌ Exception validating {org.name}: {str(e)}")
            return ConnectionValidationResult(
                organization=org.name,
                providers=providers,
                status=ValidationStatus.FAILED,
                error_message=str(e)
            )
    
    def _display_stage_discovery_results(self, org_discovery_results: Dict[str, List], organizations) -> None:
        """
        Display stage discovery results in a user-friendly format.
        
        Args:
            org_discovery_results: Dict mapping org name to discovery results
            organizations: List of organization configs
        """
        if not org_discovery_results:
            print("No discovery results to display")
            return
        
        print("\n🔍 Stage Discovery Results:")
        
        all_results = []
        for org_name, results in org_discovery_results.items():
            if len(organizations) > 1:
                print(f"\n   📋 Organization: {org_name}")
                prefix = "     "
            else:
                prefix = "   "
            
            for result in results:
                method_desc = f"({result.discovery_method} - {len(result.available_months)} months available)"
                print(f"{prefix}{result.provider}: {result.stage} {method_desc}")
                all_results.append(result)
        
        # Show overall summary
        discovery_count = sum(1 for r in all_results if r.discovery_method == "discovered")
        override_count = sum(1 for r in all_results if r.discovery_method == "override")
        stages_used = set(r.stage for r in all_results)
        
        print("\nℹ️  Discovery Summary:")
        if override_count > 0:
            print(f"   • {override_count} providers using overrides")
        if discovery_count > 0:
            print(f"   • {discovery_count} providers auto-discovered")
        
        if len(stages_used) > 1:
            print("   • Mixed stages detected - upload will proceed with best available data per provider")
        else:
            stage_name = list(stages_used)[0]
            print(f"   • All providers using {stage_name} stage")