"""
Unified execution plan service for integrated append workflow.

Creates coordinated execution plans that map provider/month combinations to
execution details, ensuring copy and upload phases operate on shared targets.
"""

from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime

from .manifest_reader import DropInfo, S3FileInfo
from .file_sequence_coordinator import FileSequenceCoordinator, SequenceState
from ..models import UploadConfig, Organization


@dataclass
class ProviderMonthContext:
    """Execution context for a single provider/month combination."""
    organization: str
    provider: str
    billing_period: str
    source_drop: Optional[DropInfo]  # None if no existing drop found
    target_drop_id: str
    target_s3_prefix: str
    source_files: List[S3FileInfo]
    starting_file_sequence: int
    sequence_state: SequenceState
    

@dataclass 
class IntegratedAppendPlan:
    """
    Unified execution plan coordinating copy and upload phases.
    
    Maps each provider/month combination to execution context that both
    copy and upload phases use to ensure coordination.
    """
    execution_contexts: Dict[str, ProviderMonthContext]  # Key: "{org}:{provider}:{month}"
    total_source_files: int
    total_providers: int
    has_copy_operations: bool
    

class IntegratedExecutionPlanService:
    """
    Service for creating unified execution plans for append operations.
    
    Single responsibility: Generate coordinated execution plans that ensure
    copy and upload phases operate on identical target locations with
    proper file sequence coordination.
    """
    
    def __init__(self, manifest_reader, drop_id_generator):
        """
        Initialize with explicit dependencies.
        
        Args:
            manifest_reader: ManifestReader for discovering source drops
            drop_id_generator: Service for generating target drop IDs
        """
        self.manifest_reader = manifest_reader
        self.drop_id_generator = drop_id_generator
        self.sequence_coordinator = FileSequenceCoordinator()
    
    def create_execution_plan(self, config: UploadConfig) -> IntegratedAppendPlan:
        """
        Create unified execution plan for integrated append workflow.
        
        Generates execution contexts for each provider/month combination,
        ensuring both copy and upload phases coordinate on shared targets.
        
        Args:
            config: Upload configuration with organizations and providers
            
        Returns:
            IntegratedAppendPlan with coordinated execution contexts
        """
        print("📋 Creating integrated append execution plan...")
        
        execution_contexts = {}
        total_source_files = 0
        has_copy_operations = False
        
        for org in config.organizations:
            # Get effective months for this organization (org.months or config.months)
            effective_months = org.months or config.months or []
            if not effective_months:
                print(f"   ⚠️  No months configured for organization {org.name}, skipping")
                continue
            
            # Get providers list for this organization
            if not org.providers:
                print(f"   ⚠️  No providers configured for organization {org.name}, skipping")
                continue
            
            # Generate single drop ID for this organization (like regular mode)
            org_drop_id = self.drop_id_generator.generate_drop_id()
            
            # Create execution contexts for all provider/month combinations
            for provider_name in org.providers:
                for month in effective_months:
                    context_key = f"{org.name}:{provider_name}:{month}"
                    
                    # Convert month to billing period format for S3 paths
                    billing_period = self._calculate_billing_period(month)
                    
                    # Create execution context for this provider/month with shared org drop ID
                    context = self._create_provider_month_context(
                        org, provider_name, billing_period, org_drop_id
                    )
                    
                    execution_contexts[context_key] = context
                    total_source_files += len(context.source_files)
                    
                    if context.source_files:
                        has_copy_operations = True
                        print(f"   📁 {org.name}/{provider_name}/{month} ({billing_period}): {len(context.source_files)} files to copy")
                    else:
                        print(f"   📁 {org.name}/{provider_name}/{month} ({billing_period}): New drop (no source files)")
        
        plan = IntegratedAppendPlan(
            execution_contexts=execution_contexts,
            total_source_files=total_source_files,
            total_providers=len(execution_contexts),
            has_copy_operations=has_copy_operations
        )
        
        print(f"📋 Plan created: {len(execution_contexts)} provider/month combinations, {total_source_files} total files to copy")
        return plan
    
    def _create_provider_month_context(
        self, 
        org: Organization, 
        provider: str, 
        month: str,
        org_drop_id: str
    ) -> ProviderMonthContext:
        """
        Create execution context for single provider/month combination.
        
        Discovers source drop, generates target drop coordinates, and
        initializes sequence coordination for copy and upload phases.
        """
        # Discover source drop from manifest.json
        source_drop = self.manifest_reader.find_active_drop(
            org.name, provider, month
        )
        
        # Get source files if drop exists
        source_files = []
        
        if source_drop:
            source_files = self.manifest_reader.list_drop_files(source_drop)
        
        # Initialize sequence state for coordination
        sequence_state = self.sequence_coordinator.initialize_sequence_state(
            provider, month, source_files
        )
        
        # Use organization drop ID aligned with regular upload mode
        target_s3_prefix = self._build_target_s3_prefix(
            org.name, provider, month, org_drop_id
        )
        
        return ProviderMonthContext(
            organization=org.name,
            provider=provider,
            billing_period=month,
            source_drop=source_drop,
            target_drop_id=org_drop_id,
            target_s3_prefix=target_s3_prefix,
            source_files=source_files,
            starting_file_sequence=sequence_state.starting_sequence,
            sequence_state=sequence_state
        )
    
    def _build_target_s3_prefix(
        self, 
        org_name: str, 
        provider: str, 
        month: str, 
        drop_id: str
    ) -> str:
        """
        Build target S3 prefix following regular mode pattern.
        
        Aligns with regular upload mode: org/provider/month/drop_id/
        """
        return f"piedpiper-custom-billing/{org_name}/{provider}/{month}/{drop_id}/"
    
    def get_context_for_provider_month(
        self, 
        plan: IntegratedAppendPlan, 
        org_name: str, 
        provider: str, 
        month: str
    ) -> Optional[ProviderMonthContext]:
        """
        Retrieve execution context for specific provider/month combination.
        
        Args:
            plan: Execution plan to search
            org_name: Organization name
            provider: Provider name
            month: Billing month
            
        Returns:
            ProviderMonthContext if found, None otherwise
        """
        context_key = f"{org_name}:{provider}:{month}"
        return plan.execution_contexts.get(context_key)
    
    def validate_execution_plan(self, plan: IntegratedAppendPlan) -> Dict[str, List[str]]:
        """
        Validate execution plan for potential issues.
        
        Checks for conflicts, missing dependencies, and coordination problems.
        
        Args:
            plan: Execution plan to validate
            
        Returns:
            Dict with validation results: {'errors': [...], 'warnings': [...]}
        """
        errors = []
        warnings = []
        
        # Check for empty plan
        if not plan.execution_contexts:
            errors.append("Execution plan is empty - no provider/month combinations found")
            return {'errors': errors, 'warnings': warnings}
        
        # Validate individual contexts
        for context_key, context in plan.execution_contexts.items():
            # Check target drop ID validity
            if not context.target_drop_id:
                errors.append(f"Missing target drop ID for {context_key}")
            
            # Check target prefix validity
            if not context.target_s3_prefix or not context.target_s3_prefix.endswith('/'):
                errors.append(f"Invalid target S3 prefix for {context_key}: {context.target_s3_prefix}")
            
            # Check file sequence coordination
            if context.source_files and context.starting_file_sequence <= len(context.source_files):
                warnings.append(f"Starting sequence {context.starting_file_sequence} may conflict with {len(context.source_files)} source files for {context_key}")
        
        # Check for drop ID uniqueness per organization (within org, drop IDs should be the same)
        org_drop_ids = {}
        for context in plan.execution_contexts.values():
            org = context.organization
            if org not in org_drop_ids:
                org_drop_ids[org] = context.target_drop_id
            elif org_drop_ids[org] != context.target_drop_id:
                errors.append(f"Inconsistent drop IDs within organization {org} - coordination will fail")
        
        return {'errors': errors, 'warnings': warnings}
    
    def get_coordinated_copy_location(
        self, 
        context: ProviderMonthContext
    ) -> str:
        """
        Get S3 prefix for copy operations.
        
        Returns identical location as upload operations to ensure coordination.
        
        Args:
            context: Provider/month execution context
            
        Returns:
            S3 prefix for copy operations (same as target_s3_prefix)
        """
        return context.target_s3_prefix
    
    def get_coordinated_upload_location(
        self, 
        context: ProviderMonthContext
    ) -> str:
        """
        Get S3 prefix for upload operations.
        
        Returns identical location as copy operations to ensure coordination.
        
        Args:
            context: Provider/month execution context
            
        Returns:
            S3 prefix for upload operations (same as target_s3_prefix)
        """
        return context.target_s3_prefix
    
    def validate_location_coordination(
        self, 
        context: ProviderMonthContext
    ) -> Dict[str, any]:
        """
        Validate that copy and upload phases target identical locations.
        
        Ensures both phases will write to the same S3 prefix to prevent
        coordination issues and file conflicts.
        
        Args:
            context: Provider/month execution context
            
        Returns:
            Validation result dict with 'valid' boolean and 'issues' list
        """
        copy_location = self.get_coordinated_copy_location(context)
        upload_location = self.get_coordinated_upload_location(context)
        
        issues = []
        
        # Check location identity
        if copy_location != upload_location:
            issues.append(f"Copy location '{copy_location}' != upload location '{upload_location}'")
        
        # Check location validity
        if not copy_location or not copy_location.endswith('/'):
            issues.append(f"Invalid copy location format: '{copy_location}'")
        
        if not upload_location or not upload_location.endswith('/'):
            issues.append(f"Invalid upload location format: '{upload_location}'")
        
        # Check location follows regular mode pattern (should contain the base path but include drop ID)
        expected_base_pattern = f"piedpiper-custom-billing/{context.organization}/{context.provider}/{context.billing_period}/"
        if expected_base_pattern not in copy_location:
            issues.append(f"Location doesn't follow regular mode pattern: expected to contain '{expected_base_pattern}', got '{copy_location}'")
        
        return {
            'valid': len(issues) == 0,
            'issues': issues,
            'copy_location': copy_location,
            'upload_location': upload_location,
            'locations_identical': copy_location == upload_location
        }
    
    def _calculate_billing_period(self, month: str) -> str:
        """
        Calculate billing period from month string.
        
        Args:
            month: Month in YYYY-MM format (e.g., "2025-04")
            
        Returns:
            Billing period in YYYYMMDD-YYYYMMDD format (e.g., "20250401-20250501")
        """
        # Parse month
        year, month_num = month.split('-')
        year = int(year)
        month_num = int(month_num)
        
        # Calculate billing period (first day of month to first day of next month)
        start_date = f"{year:04d}{month_num:02d}01"
        
        # Calculate next month
        if month_num == 12:
            next_year = year + 1
            next_month = 1
        else:
            next_year = year
            next_month = month_num + 1
        
        end_date = f"{next_year:04d}{next_month:02d}01"
        
        return f"{start_date}-{end_date}"


class SimpleDropIdGenerator:
    """
    Simple drop ID generator using timestamp.
    
    Generates unique drop IDs following existing patterns.
    """
    
    def generate_drop_id(self) -> str:
        """Generate unique drop ID using timestamp."""
        return datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")