"""Fallback manager and specialized managers."""

import asyncio
import json
import logging
from typing import Any, Callable, Dict, List, Optional

from ...errors import TraxError, ErrorCode, ErrorCategory, ErrorSeverity
from ...logging import get_logger
from .base import FallbackConfig, FallbackProvider
from .providers import (
    CacheFallbackProvider,
    ServiceFallbackProvider,
    DefaultValueFallbackProvider
)

logger = get_logger(__name__)


class FallbackManager:
    """Manages multiple fallback providers."""
    
    def __init__(self, config: Optional[FallbackConfig] = None):
        self.config = config or FallbackConfig()
        self.providers: List[FallbackProvider] = []
        self.logger = get_logger(f"{__name__}.FallbackManager")
    
    def add_provider(self, provider: FallbackProvider):
        """Add a fallback provider."""
        self.providers.append(provider)
        # Sort by priority (lower number = higher priority)
        self.providers.sort(key=lambda p: p.priority)
        self.logger.info(f"Added fallback provider: {provider.name} (priority: {provider.priority})")
    
    def remove_provider(self, provider_name: str):
        """Remove a fallback provider by name."""
        self.providers = [p for p in self.providers if p.name != provider_name]
        self.logger.info(f"Removed fallback provider: {provider_name}")
    
    async def execute_with_fallbacks(self, operation: Callable, *args, **kwargs) -> Any:
        """Execute operation with fallback support."""
        if not self.config.enabled:
            return await operation(*args, **kwargs)
        
        # Try primary operation first
        try:
            result = await asyncio.wait_for(operation(*args, **kwargs), timeout=self.config.timeout)
            return result
        except Exception as e:
            self.logger.warning(f"Primary operation failed: {e}")
            
            # Try fallback providers
            return await self._try_fallbacks(*args, **kwargs)
    
    async def _try_fallbacks(self, *args, **kwargs) -> Any:
        """Try fallback providers in order of priority."""
        fallback_errors = []
        
        for provider in self.providers[:self.config.max_fallbacks]:
            if not await provider.is_available():
                self.logger.debug(f"Fallback provider {provider.name} not available")
                continue
            
            try:
                if self.config.log_fallback_attempts:
                    self.logger.info(f"Trying fallback provider: {provider.name}")
                
                result = await asyncio.wait_for(
                    provider.execute(*args, **kwargs),
                    timeout=self.config.timeout
                )
                
                if result is not None:
                    self.logger.info(f"Fallback successful with provider: {provider.name}")
                    return result
                else:
                    self.logger.warning(f"Fallback provider {provider.name} returned None")
                    
            except Exception as e:
                error_msg = f"Fallback provider {provider.name} failed: {e}"
                fallback_errors.append(error_msg)
                self.logger.warning(error_msg)
        
        # All fallbacks failed
        error_msg = f"All fallback providers failed. Errors: {fallback_errors}"
        self.logger.error(error_msg)
        raise TraxError(error_msg, error_code=ErrorCode.RECOVERY_FAILED)
    
    async def get_health_status(self) -> Dict[str, Any]:
        """Get health status of all providers."""
        status = {
            "enabled": self.config.enabled,
            "providers": []
        }
        
        for provider in self.providers:
            try:
                provider_status = await provider.get_health_status()
                status["providers"].append(provider_status)
            except Exception as e:
                status["providers"].append({
                    "name": provider.name,
                    "available": False,
                    "error": str(e)
                })
        
        return status


# Specific fallback implementations for common scenarios
class TranscriptionFallbackManager(FallbackManager):
    """Specialized fallback manager for transcription operations."""
    
    def __init__(self):
        super().__init__(FallbackConfig(
            enabled=True,
            max_fallbacks=2,
            timeout=60.0,
            cache_ttl=7200,  # 2 hours for transcriptions
            retry_on_fallback_failure=True
        ))
    
    async def add_whisper_fallback(self, whisper_service: Callable):
        """Add Whisper transcription as fallback."""
        provider = ServiceFallbackProvider("whisper", "whisper", whisper_service)
        self.add_provider(provider)
    
    async def add_cached_transcription_fallback(self, cache_store: Callable, cache_retrieve: Callable):
        """Add cached transcription as fallback."""
        provider = CacheFallbackProvider("cached_transcription", cache_store, cache_retrieve)
        self.add_provider(provider)
    
    async def add_basic_transcription_fallback(self, basic_service: Callable):
        """Add basic transcription service as fallback."""
        provider = ServiceFallbackProvider("basic", "basic_transcription", basic_service)
        self.add_provider(provider)


class MediaDownloadFallbackManager(FallbackManager):
    """Specialized fallback manager for media download operations."""
    
    def __init__(self):
        super().__init__(FallbackConfig(
            enabled=True,
            max_fallbacks=3,
            timeout=120.0,
            cache_ttl=86400,  # 24 hours for downloads
            retry_on_fallback_failure=True
        ))
    
    async def add_alternative_downloader(self, downloader_name: str, downloader_service: Callable):
        """Add alternative download service."""
        provider = ServiceFallbackProvider(downloader_name, downloader_name, downloader_service)
        self.add_provider(provider)
    
    async def add_cached_download_fallback(self, cache_store: Callable, cache_retrieve: Callable):
        """Add cached download as fallback."""
        provider = CacheFallbackProvider("cached_download", cache_store, cache_retrieve)
        self.add_provider(provider)
    
    async def add_mirror_fallback(self, mirror_service: Callable):
        """Add mirror download service as fallback."""
        provider = ServiceFallbackProvider("mirror", "mirror", mirror_service)
        self.add_provider(provider)


class APIFallbackManager(FallbackManager):
    """Specialized fallback manager for API operations."""
    
    def __init__(self):
        super().__init__(FallbackConfig(
            enabled=True,
            max_fallbacks=2,
            timeout=30.0,
            cache_ttl=1800,  # 30 minutes for API responses
            retry_on_fallback_failure=True
        ))
    
    async def add_alternative_api(self, api_name: str, api_service: Callable):
        """Add alternative API service."""
        provider = ServiceFallbackProvider(api_name, api_name, api_service)
        self.add_provider(provider)
    
    async def add_cached_api_response_fallback(self, cache_store: Callable, cache_retrieve: Callable):
        """Add cached API response as fallback."""
        provider = CacheFallbackProvider("cached_api_response", cache_store, cache_retrieve)
        self.add_provider(provider)
    
    async def add_default_response_fallback(self, default_response: Any):
        """Add default response as fallback."""
        provider = DefaultValueFallbackProvider("default_response", default_response)
        self.add_provider(provider)
