"""Parallel processing system for Trax platform.

This module provides parallel processing capabilities for diarization and transcription,
using ThreadPoolExecutor to reduce processing time while maintaining accuracy.
"""

import logging
import threading
import time
from pathlib import Path
from typing import Any, Dict, List, Optional

from ..base.services import BaseService
from .diarization_types import (
    DiarizationConfig, DiarizationResult, ProcessingResult,
    ParallelProcessingConfig, ParallelProcessorProtocol,
    ParallelProcessingError
)
from .parallel_processing_utils import (
    initialize_services, execute_parallel_tasks, merge_processing_results,
    process_batch_files, update_processing_statistics, calculate_success_rate,
    estimate_processing_speedup
)

logger = logging.getLogger(__name__)


class ParallelProcessor(BaseService):
    """Manages parallel processing of diarization and transcription.
    
    Uses ThreadPoolExecutor to run diarization and transcription simultaneously,
    reducing overall processing time while maintaining accuracy.
    """
    
    def __init__(self, config: Optional[ParallelProcessingConfig] = None):
        """Initialize the ParallelProcessor.
        
        Args:
            config: Configuration for parallel processing
        """
        super().__init__(name="ParallelProcessor")
        self.config = config or ParallelProcessingConfig()
        
        # Initialize services
        self.diarization_manager = None
        self.transcription_service = None
        
        # Thread pool for parallel execution
        import concurrent.futures
        self.executor = concurrent.futures.ThreadPoolExecutor(
            max_workers=self.config.max_workers,
            thread_name_prefix="TraxProcessor"
        )
        
        # Processing statistics
        self.stats = {
            "total_files_processed": 0,
            "successful_processing": 0,
            "failed_processing": 0,
            "total_processing_time": 0.0,
            "average_processing_time": 0.0,
            "parallel_speedup": 0.0
        }
        
        # Thread safety
        self._lock = threading.Lock()
        self._active_tasks: Dict[str, Any] = {}
        
        logger.info(f"ParallelProcessor initialized with {self.config.max_workers} workers")
    
    async def _initialize_impl(self) -> None:
        """Initialize the parallel processor."""
        try:
            # Initialize services
            self.diarization_manager, self.transcription_service = initialize_services(
                self.diarization_manager, self.transcription_service
            )
            logger.info("ParallelProcessor initialized successfully")
        except Exception as e:
            logger.error(f"Failed to initialize ParallelProcessor: {e}")
            raise
    
    def process_file(
        self, 
        audio_path: Path,
        diarization_config: Optional[DiarizationConfig] = None,
        transcription_config: Optional[Any] = None
    ) -> ProcessingResult:
        """Process a single file with parallel diarization and transcription.
        
        Args:
            audio_path: Path to the audio file
            diarization_config: Configuration for diarization
            transcription_config: Configuration for transcription
            
        Returns:
            ProcessingResult with both diarization and transcription results
            
        Raises:
            ParallelProcessingError: If processing fails
        """
        if not audio_path.exists():
            raise ParallelProcessingError(f"Audio file not found: {audio_path}")
        
        # Initialize services if needed
        self.diarization_manager, self.transcription_service = initialize_services(
            self.diarization_manager, self.transcription_service
        )
        
        task_id = f"task_{int(time.time() * 1000)}"
        
        try:
            logger.info(f"Starting parallel processing for: {audio_path.name}")
            
            start_time = time.time()
            
            # Execute parallel tasks
            diarization_result, transcription_result = execute_parallel_tasks(
                audio_path,
                self.diarization_manager,
                self.transcription_service,
                diarization_config,
                transcription_config,
                self.config.timeout_seconds
            )
            
            # Merge results
            merged_result = merge_processing_results(diarization_result, transcription_result)
            
            processing_time = time.time() - start_time
            
            # Create result
            result = ProcessingResult(
                task_id=task_id,
                diarization_result=diarization_result,
                transcription_result=transcription_result,
                merged_result=merged_result,
                processing_time=processing_time,
                success=True
            )
            
            # Update statistics
            update_processing_statistics(self.stats, processing_time, True, self._lock)
            
            logger.info(f"Parallel processing completed: {processing_time:.2f}s")
            return result
            
        except Exception as e:
            logger.error(f"Parallel processing failed for {audio_path}: {e}")
            
            # Update statistics
            update_processing_statistics(self.stats, 0.0, False, self._lock)
            
            return ProcessingResult(
                task_id=task_id,
                processing_time=0.0,
                success=False,
                error_message=str(e)
            )
    
    def process_batch(
        self, 
        audio_paths: List[Path],
        configs: Optional[Dict[str, Any]] = None
    ) -> List[ProcessingResult]:
        """Process multiple files in parallel.
        
        Args:
            audio_paths: List of audio file paths
            configs: Optional configuration overrides
            
        Returns:
            List of ProcessingResult objects
        """
        return process_batch_files(
            audio_paths, self, configs, self.config.timeout_seconds
        )
    
    def get_processing_stats(self) -> Dict[str, Any]:
        """Get statistics about processing performance."""
        with self._lock:
            stats = self.stats.copy()
            stats["success_rate"] = calculate_success_rate(stats)
            return stats
    
    def estimate_speedup(self, sequential_time: float, parallel_time: float) -> float:
        """Estimate the speedup achieved by parallel processing.
        
        Args:
            sequential_time: Time for sequential processing
            parallel_time: Time for parallel processing
            
        Returns:
            Speedup ratio (sequential_time / parallel_time)
        """
        return estimate_processing_speedup(
            sequential_time, parallel_time, self.stats, self._lock
        )
    
    def cleanup(self):
        """Clean up resources and shutdown thread pool."""
        try:
            # Shutdown thread pool
            self.executor.shutdown(wait=True)
            
            # Clean up services
            if self.diarization_manager:
                self.diarization_manager.cleanup()
            
            logger.info("ParallelProcessor cleanup completed")
            
        except Exception as e:
            logger.error(f"Failed to cleanup ParallelProcessor: {e}")


# Factory function for creating parallel processors
def create_parallel_processor(
    config: Optional[ParallelProcessingConfig] = None
) -> ParallelProcessor:
    """Create a new parallel processor instance.
    
    Args:
        config: Optional configuration for the processor
        
    Returns:
        Configured ParallelProcessor instance
    """
    return ParallelProcessor(config)
