"""
Time-shift orchestrator service.

Thin coordinator that delegates to specialized services for time-shifting operations.
Uses provider copy optimization to avoid re-transforming data for multiple orgs.
"""

import uuid
from typing import List, Optional
from ..models.time_shift_models import TimeShiftConfig, TimeShiftResult, DropCopyResult
from .time_shift_config_loader import TimeShiftConfigLoader
from .target_month_parser import TargetMonthParser
from .source_month_selector import SourceMonthSelector
from .drop_copier import DropCopier
from .time_shift_provider_copier import TimeShiftProviderCopier


class TimeShiftOrchestrator:
    """
    Orchestrates time-shift operation (thin coordinator).

    Uses provider copy optimization: transforms first org, then copies to remaining orgs.
    Delegates all work to specialized services following Single Responsibility Principle.
    """

    def __init__(
        self,
        config_loader: TimeShiftConfigLoader,
        target_parser: TargetMonthParser,
        source_selector: SourceMonthSelector,
        drop_copier: DropCopier,
        provider_copier: Optional[TimeShiftProviderCopier] = None
    ):
        """
        Initialize orchestrator with all dependencies.

        Args:
            config_loader: Configuration loader service
            target_parser: Target month parser service
            source_selector: Source month selector service
            drop_copier: Drop copier service
            provider_copier: Provider copier service (for optimization)
        """
        self.config_loader = config_loader
        self.target_parser = target_parser
        self.source_selector = source_selector
        self.drop_copier = drop_copier
        self.provider_copier = provider_copier

    def execute(self, config_path: str) -> TimeShiftResult:
        """
        Execute time-shift operation (pure delegation).

        Args:
            config_path: Path to time-shift configuration file

        Returns:
            TimeShiftResult with operation statistics

        Raises:
            ValueError: If configuration is invalid
            RuntimeError: If any drop fails
        """
        import time
        start_time = time.time()

        # Generate unique job ID
        job_id = self._generate_job_id()

        # 1. Load configuration
        config = self.config_loader.load_config(config_path)

        # 2. Parse target months
        target_months = self.target_parser.parse_targets(
            explicit_months=config.target_months,
            month_range=config.target_range
        )

        # 3. Select source month
        orgs = list(config.organizations.keys())
        source_month = self.source_selector.select_source_month(
            orgs=orgs,
            providers=config.providers,
            user_specified=config.source_month
        )

        # 4. Process all combinations
        drop_results = self._process_all_drops(
            orgs=orgs,
            providers=config.providers,
            source_month=source_month,
            target_months=target_months
        )

        # 5. Aggregate results
        total_time = time.time() - start_time

        return self._build_result(
            job_id=job_id,
            source_month=source_month,
            target_months=target_months,
            orgs=orgs,
            providers=config.providers,
            drop_results=drop_results,
            total_time=total_time
        )

    def _process_all_drops(
        self,
        orgs: List[str],
        providers: List[str],
        source_month: str,
        target_months: List[str]
    ) -> List[DropCopyResult]:
        """
        Process all org/provider/target_month combinations with optimization.

        Uses provider copy optimization if multiple orgs:
        1. Time-shift first org for all providers/months
        2. S3-to-S3 copy to remaining orgs (no transformation)

        Args:
            orgs: Organizations to process
            providers: Providers to process
            source_month: Source month
            target_months: Target months

        Returns:
            List of DropCopyResults
        """
        if len(orgs) <= 1 or not self.provider_copier:
            # Single org or no provider copier - use direct transform
            return self._process_without_optimization(
                orgs, providers, source_month, target_months
            )

        # Multiple orgs - use provider copy optimization
        return self._process_with_optimization(
            orgs, providers, source_month, target_months
        )

    def _process_without_optimization(
        self,
        orgs: List[str],
        providers: List[str],
        source_month: str,
        target_months: List[str]
    ) -> List[DropCopyResult]:
        """Process all combinations without optimization."""
        results = []

        for org in orgs:
            for provider in providers:
                for target_month in target_months:
                    result = self._process_single_drop(
                        org, provider, source_month, target_month
                    )
                    results.append(result)

        return results

    def _process_with_optimization(
        self,
        orgs: List[str],
        providers: List[str],
        source_month: str,
        target_months: List[str]
    ) -> List[DropCopyResult]:
        """
        Process with provider-by-provider copy optimization.

        For each provider:
          1. Time-shift first org for all target months
          2. Copy to remaining orgs immediately

        Benefits:
        - Earlier results (can use AWS data while Azure still processing)
        - Better failure recovery (partial success per provider)
        - Lower memory footprint (one provider at a time)
        - Natural parallelization boundary (each provider is independent unit)
        """
        results = []
        first_org = orgs[0]
        remaining_orgs = orgs[1:]

        # Process one provider at a time (transform + copy)
        for provider_idx, provider in enumerate(providers, 1):
            print(f"\n   📊 Provider {provider_idx}/{len(providers)}: {provider}\n")

            # Phase 1: Time-shift first org for all months of this provider
            print(f"   🔄 Phase 1: Time-shifting {first_org}/{provider} (transform)")
            for target_month in target_months:
                result = self._process_single_drop(
                    first_org, provider, source_month, target_month
                )
                results.append(result)

            # Phase 2: Copy this provider to remaining orgs immediately
            if remaining_orgs:
                print(f"\n   📋 Phase 2: Copying {provider} to {len(remaining_orgs)} orgs (S3-to-S3)")
                for target_month in target_months:
                    copy_results = self.provider_copier.copy_provider_to_orgs(
                        source_org=first_org,
                        target_orgs=remaining_orgs,
                        provider=provider,
                        source_month=source_month,
                        target_month=target_month
                    )
                    results.extend(copy_results)

                    for result in copy_results:
                        if result.success:
                            print(
                                f"   📋 Copied: {result.org}/{provider} "
                                f"{source_month} → {target_month} "
                                f"({result.files_copied} files, {result.processing_time_sec:.1f}s)"
                            )
                        else:
                            print(f"   ❌ Failed: {result.org}/{provider} - {result.error_message}")

                print(f"   ✅ {provider} complete: {first_org} transformed + {len(remaining_orgs)} orgs copied\n")
            else:
                print(f"   ✅ {provider} complete: {first_org} transformed (no orgs to copy)\n")

        return results

    def _process_single_drop(
        self, org: str, provider: str, source_month: str, target_month: str
    ) -> DropCopyResult:
        """Process single drop and log result."""
        print(
            f"   🔄 Processing: {org}/{provider} "
            f"{source_month} → {target_month}"
        )

        result = self.drop_copier.copy_drop(
            org=org,
            provider=provider,
            source_month=source_month,
            target_month=target_month
        )

        if result.success:
            print(
                f"   ✅ Success: {result.files_copied} files, "
                f"{result.total_records:,} records, "
                f"{result.processing_time_sec:.1f}s"
            )
            print(
                f"      📄 Source: {result.source_drop_id} "
                f"→ Target: {result.target_drop_id}"
            )
        else:
            print(f"   ❌ Failed: {result.error_message}")

        return result

    def _build_result(
        self,
        job_id: str,
        source_month: str,
        target_months: List[str],
        orgs: List[str],
        providers: List[str],
        drop_results: List[DropCopyResult],
        total_time: float
    ) -> TimeShiftResult:
        """
        Build aggregated result from drop results.

        Args:
            job_id: Job identifier
            source_month: Source month
            target_months: Target months
            orgs: Organizations processed
            providers: Providers processed
            drop_results: Individual drop results
            total_time: Total processing time

        Returns:
            TimeShiftResult with aggregated statistics
        """
        successful = [r for r in drop_results if r.success]
        failed = [r for r in drop_results if not r.success]

        total_records = sum(r.total_records for r in successful)
        total_bytes = sum(r.total_bytes for r in successful)

        return TimeShiftResult(
            job_id=job_id,
            source_month=source_month,
            target_months=target_months,
            organizations=orgs,
            providers=providers,
            drops_processed=len(successful),
            drops_failed=len(failed),
            total_records=total_records,
            total_bytes=total_bytes,
            total_time_sec=total_time,
            drop_results=drop_results
        )

    def _generate_job_id(self) -> str:
        """
        Generate unique job identifier.

        Returns:
            Job ID string
        """
        return f"time-shift-{uuid.uuid4().hex[:12]}"
