"""
Integrated append orchestrator with provider/month coordination.

Replaces separate copy + upload workflows with unified execution that
ensures both phases coordinate on shared target drops with proper sequencing.
"""

from typing import Dict, List, Any, Optional
from dataclasses import dataclass

from .manifest_reader import ManifestReader
from .s3_file_copier import S3FileCopier
from .integrated_execution_plan import IntegratedExecutionPlanService, IntegratedAppendPlan, ProviderMonthContext
from .manifest_manager import ManifestManager
from ..models import UploadConfig, OrganizationResult
from src.services.upload_workflow_methods import UploadWorkflowMethods


@dataclass
class IntegratedAppendResult:
    """Result from integrated append operation for a single provider/month."""
    organization: str
    provider: str
    billing_period: str
    target_drop_id: str
    files_copied: int
    files_uploaded: int
    copy_errors: List[str]
    upload_errors: List[str]
    status: str  # success, partial, failed


class IntegratedAppendOrchestrator:
    """
    Orchestrator for integrated append workflow with provider/month coordination.
    
    Single responsibility: Execute unified append workflow where copy and upload
    phases coordinate on shared target drops with proper file sequencing.
    """
    
    def __init__(
        self,
        manifest_reader: ManifestReader,
        s3_file_copier: S3FileCopier,
        execution_plan_service: IntegratedExecutionPlanService,
        upload_workflow: UploadWorkflowMethods,
        manifest_manager: ManifestManager,
        s3_formatter
    ):
        """
        Initialize with dependencies.
        
        Args:
            manifest_reader: Service for reading manifest.json files
            s3_file_copier: Service for S3-to-S3 copy operations
            execution_plan_service: Service for creating integrated execution plans
            upload_workflow: Existing upload workflow methods
            manifest_manager: Service for coordinated manifest updates
            s3_formatter: S3 formatting utilities
        """
        self.manifest_reader = manifest_reader
        self.s3_file_copier = s3_file_copier
        self.execution_plan_service = execution_plan_service
        self.upload_workflow = upload_workflow
        self.manifest_manager = manifest_manager
        self.s3_formatter = s3_formatter
    
    def execute_integrated_append_workflow(self, config: UploadConfig, dry_run: bool = False) -> Dict[str, OrganizationResult]:
        """
        Execute integrated append workflow with provider/month coordination.
        
        Creates unified execution plan and processes provider/month combinations
        with coordinated copy and upload phases sharing target drops.
        
        Args:
            config: Upload configuration with organizations
            dry_run: Whether to perform validation only
            
        Returns:
            Dict mapping organization names to OrganizationResult objects
        """
        if not config.is_append_mode():
            raise ValueError("Integrated append workflow requires append_mode=True")
        
        print(f"🔄 Starting integrated append workflow ({'dry-run' if dry_run else 'live mode'})")
        
        # Create unified execution plan
        execution_plan = self.execution_plan_service.create_execution_plan(config)
        
        # Validate execution plan
        validation_result = self.execution_plan_service.validate_execution_plan(execution_plan)
        if validation_result['errors']:
            error_msg = f"Execution plan validation failed: {'; '.join(validation_result['errors'])}"
            print(f"   ❌ {error_msg}")
            raise RuntimeError(error_msg)
        
        if validation_result['warnings']:
            for warning in validation_result['warnings']:
                print(f"   ⚠️  Warning: {warning}")
        
        if dry_run:
            return self._execute_dry_run_with_plan(execution_plan, config)
        else:
            return self._execute_live_workflow_with_plan(execution_plan, config)
    
    def _execute_dry_run_with_plan(self, execution_plan: IntegratedAppendPlan, config: UploadConfig) -> Dict[str, OrganizationResult]:
        """Execute dry-run validation using execution plan."""
        print("🧪 Dry-run validation of integrated append workflow")
        
        results = {}
        org_contexts = self._group_contexts_by_organization(execution_plan)
        
        for org_name, contexts in org_contexts.items():
            print(f"\n🏢 Validating organization: {org_name}")
            
            total_copy_files = sum(len(ctx.source_files) for ctx in contexts)
            provider_count = len(contexts)
            
            print(f"   📋 {provider_count} provider/month combinations")
            print(f"   📁 {total_copy_files} total files to copy")
            
            # Simulate provider/month validation
            for context in contexts:
                if context.source_files:
                    print(f"   ✓ {context.provider}/{context.billing_period}: {len(context.source_files)} files → sequence {context.starting_file_sequence}")
                else:
                    print(f"   ✓ {context.provider}/{context.billing_period}: New drop (no copy operations)")
            
            # Create validation result
            results[org_name] = OrganizationResult(
                organization=org_name,
                status="success",
                files_uploaded=0,  # Dry run
                providers=[ctx.provider for ctx in contexts]
            )
        
        print(f"\n🧪 Dry-run validation complete - integrated append workflow is ready")
        return results
    
    def _execute_live_workflow_with_plan(self, execution_plan: IntegratedAppendPlan, config: UploadConfig) -> Dict[str, OrganizationResult]:
        """Execute live integrated append workflow using execution plan."""
        print("🚀 Executing integrated append workflow")
        
        results = {}
        org_contexts = self._group_contexts_by_organization(execution_plan)
        
        for org_name, contexts in org_contexts.items():
            print(f"\n🏢 Processing organization: {org_name}")
            
            try:
                # Execute integrated workflow for each provider/month combination
                append_results = []
                
                for context in contexts:
                    result = self._execute_integrated_provider_month(context, config)
                    append_results.append(result)
                
                # Aggregate organization results
                org_result = self._aggregate_organization_results(org_name, append_results, contexts)
                results[org_name] = org_result
                
            except Exception as e:
                print(f"   ❌ Integrated workflow failed for {org_name}: {str(e)}")
                results[org_name] = OrganizationResult(
                    organization=org_name,
                    status="failed",
                    error_message=f"Integrated append workflow failed: {str(e)}",
                    providers=[ctx.provider for ctx in contexts]
                )
        
        return results
    
    def _execute_integrated_provider_month(self, context: ProviderMonthContext, config: UploadConfig) -> IntegratedAppendResult:
        """
        Execute single integrated append operation for provider/month combination.
        
        This is the core integration method that replaces separate copy + upload
        workflows with coordinated single execution using shared target drops.
        """
        print(f"   📋 Processing {context.provider}/{context.billing_period} → drop {context.target_drop_id}")
        
        copy_errors = []
        upload_errors = []
        files_copied = 0
        files_uploaded = 0
        
        try:
            # Validate location coordination before proceeding
            location_validation = self.execution_plan_service.validate_location_coordination(context)
            if not location_validation['valid']:
                error_msg = f"Location coordination failed: {'; '.join(location_validation['issues'])}"
                raise RuntimeError(error_msg)
            
            # Get coordinated target location for both phases
            target_location = self.execution_plan_service.get_coordinated_copy_location(context)
            
            # Phase 1: Copy existing files with coordinated sequence numbers
            if context.source_files:
                print(f"      📁 Copying {len(context.source_files)} existing files to {target_location}...")
                
                # Use sequence coordinator for copy filenames
                copy_filenames = context.sequence_state.copied_files
                
                copy_results = self.s3_file_copier.copy_files_parallel(
                    context.source_files,
                    target_location,
                    skip_existing=False  # Integrated workflow controls all sequencing
                )
                
                # Update sequence state
                files_copied = sum(1 for result in copy_results if result.success)
                copy_errors = [result.error_message for result in copy_results if not result.success and result.error_message]
                context.sequence_state.copied_files = files_copied
                
                print(f"      ✓ Copied {files_copied}/{len(context.source_files)} files")
                if copy_errors:
                    print(f"      ⚠️  {len(copy_errors)} copy errors")
                
                # Validate sequence coordination after copy
                sequence_validation = context.sequence_state
                copy_start, copy_end = sequence_validation.get_copy_sequence_range if hasattr(sequence_validation, 'get_copy_sequence_range') else (1, files_copied)
                print(f"      📊 Copy sequences: {copy_start}-{copy_end}")
            else:
                print(f"      ℹ️  No existing files to copy - creating new drop")
            
            # Phase 2: Upload new data with coordinated sequence continuation
            upload_start_seq = context.sequence_state.starting_sequence
            print(f"      📤 Uploading new data to {target_location} starting from sequence {upload_start_seq}...")
            
            # Create provider-specific config with coordinated target location
            provider_config = self._create_coordinated_provider_config(config, context, target_location)
            
            # Execute upload phase with sequence coordination and coordinated drop ID
            upload_result = self.upload_workflow.execute_upload_workflow(
                provider_config, 
                dry_run=False, 
                coordinated_drop_id=context.target_drop_id
            )
            
            # Extract and coordinate upload results
            org_upload_result = upload_result.results.get(context.organization)
            if org_upload_result and org_upload_result.is_successful():
                files_uploaded = org_upload_result.files_uploaded
                context.sequence_state.uploaded_files = files_uploaded
                print(f"      ✓ Uploaded {files_uploaded} new files (sequences {upload_start_seq}+)")
            elif org_upload_result:
                files_uploaded = org_upload_result.files_uploaded
                context.sequence_state.uploaded_files = files_uploaded
                upload_errors.append(org_upload_result.error_message or "Upload completed with errors")
                print(f"      ⚠️  Upload partial: {files_uploaded} files, errors present")
            else:
                upload_errors.append("Upload workflow returned no results")
                print(f"      ❌ Upload workflow failed")
            
            # Phase 3: Update manifest to reflect both copied and uploaded files
            if (files_copied > 0 or files_uploaded > 0) and not copy_errors and not upload_errors:
                print(f"      📄 Updating manifest for integrated operation...")
                
                # Create upload results for manifest manager (simulated from upload phase)
                upload_results = []
                if files_uploaded > 0:
                    # Simulate upload results - in real integration this would come from upload workflow
                    for i in range(files_uploaded):
                        seq_num = context.sequence_state.starting_sequence + i
                        upload_results.append({
                            'filename': f'data-export-{seq_num:04d}.csv.gz',
                            'size_mb': 0,  # Would be populated by real upload workflow
                            'rows': 0      # Would be populated by real upload workflow  
                        })
                
                manifest_result = self.manifest_manager.update_manifest_for_append_operation(
                    context.organization,
                    context.provider, 
                    context.billing_period,
                    context.target_drop_id,
                    context.source_files,  # Files that were copied
                    upload_results         # Files that were uploaded
                )
                
                if manifest_result['status'] != 'success':
                    upload_errors.append(f"Manifest update failed: {manifest_result.get('error', 'Unknown error')}")
                    print(f"      ⚠️  Manifest update failed")
                else:
                    print(f"      ✓ Manifest updated: {manifest_result['total_files']} total files")
            
            # Final sequence coordination validation
            final_validation = context.sequence_state
            print(f"      📊 Final state: {files_copied} copied + {files_uploaded} uploaded = {files_copied + files_uploaded} total files")
            
            # Determine integrated operation status
            if copy_errors or upload_errors:
                if files_copied > 0 or files_uploaded > 0:
                    status = "partial"
                else:
                    status = "failed"
            else:
                status = "success"
            
            return IntegratedAppendResult(
                organization=context.organization,
                provider=context.provider,
                billing_period=context.billing_period,
                target_drop_id=context.target_drop_id,
                files_copied=files_copied,
                files_uploaded=files_uploaded,
                copy_errors=copy_errors,
                upload_errors=upload_errors,
                status=status
            )
            
        except Exception as e:
            error_msg = f"Integrated append operation failed: {str(e)}"
            print(f"      ❌ {error_msg}")
            
            return IntegratedAppendResult(
                organization=context.organization,
                provider=context.provider,
                billing_period=context.billing_period,
                target_drop_id=context.target_drop_id,
                files_copied=0,
                files_uploaded=0,
                copy_errors=[error_msg],
                upload_errors=[],
                status="failed"
            )
    
    def _create_coordinated_provider_config(
        self, 
        config: UploadConfig, 
        context: ProviderMonthContext, 
        target_location: str
    ) -> UploadConfig:
        """
        Create coordinated single-provider configuration for upload workflow.
        
        Extracts organization and provider configuration for isolated processing
        with coordinated target location to ensure integration with copy phase.
        """
        from dataclasses import replace
        
        # Find the organization in the config
        target_org = None
        for org in config.organizations:
            if org.name == context.organization:
                target_org = org
                break
        
        if not target_org:
            raise ValueError(f"Organization {context.organization} not found in config")
        
        # Create single-provider organization
        # Note: Use original month format for upload workflow, not billing period format
        original_month = self._billing_period_to_month(context.billing_period)
        single_provider_org = replace(
            target_org,
            providers=[context.provider],  # Single provider in the list
            months=[original_month]
        )
        
        # Create coordinated single-org config for upload workflow
        # This ensures upload phase targets same location as copy phase
        coordinated_config = replace(
            config,
            organizations=[single_provider_org],
            months=[context.billing_period],
            append_mode=False,  # Don't trigger append mode in upload phase
            # Note: Upload workflow will use context.target_drop_id for coordination
        )
        
        return coordinated_config
    
    def _billing_period_to_month(self, billing_period: str) -> str:
        """
        Convert billing period back to month format for upload workflow compatibility.
        
        Args:
            billing_period: Billing period in YYYYMMDD-YYYYMMDD format (e.g., "20250401-20250501")
            
        Returns:
            Month in YYYY-MM format (e.g., "2025-04")
        """
        # Extract start date from billing period
        start_date = billing_period.split('-')[0]  # "20250401"
        year = start_date[:4]  # "2025"
        month = start_date[4:6]  # "04"
        
        return f"{year}-{month}"
    
    def _group_contexts_by_organization(self, execution_plan: IntegratedAppendPlan) -> Dict[str, List[ProviderMonthContext]]:
        """Group execution contexts by organization for processing."""
        org_contexts = {}
        
        for context in execution_plan.execution_contexts.values():
            if context.organization not in org_contexts:
                org_contexts[context.organization] = []
            org_contexts[context.organization].append(context)
        
        return org_contexts
    
    def _aggregate_organization_results(
        self, 
        org_name: str, 
        append_results: List[IntegratedAppendResult], 
        contexts: List[ProviderMonthContext]
    ) -> OrganizationResult:
        """
        Aggregate provider/month results into organization result.
        
        Combines results from all provider/month combinations into single
        organization result compatible with existing workflow interface.
        """
        total_copied = sum(result.files_copied for result in append_results)
        total_uploaded = sum(result.files_uploaded for result in append_results)
        total_files = total_copied + total_uploaded
        
        # Collect all errors
        all_errors = []
        for result in append_results:
            all_errors.extend(result.copy_errors)
            all_errors.extend(result.upload_errors)
        
        # Determine overall status
        successful_results = sum(1 for result in append_results if result.status == "success")
        partial_results = sum(1 for result in append_results if result.status == "partial")
        
        if successful_results == len(append_results):
            status = "success"
        elif successful_results > 0 or partial_results > 0:
            status = "partial"
        else:
            status = "failed"
        
        # Status reporting
        if status == "success":
            print(f"   ✅ {org_name}: {total_copied} copied + {total_uploaded} uploaded = {total_files} files")
        elif status == "partial":
            print(f"   ⚠️  {org_name}: {total_files} completed, {len(all_errors)} errors")
        else:
            print(f"   ❌ {org_name}: All operations failed")
        
        return OrganizationResult(
            organization=org_name,
            status=status,
            files_uploaded=total_files,
            files_failed=len(all_errors),
            providers=[ctx.provider for ctx in contexts],
            failed_files=all_errors if all_errors else None,
            error_message="; ".join(all_errors) if all_errors else None
        )