"""Concrete recovery strategy implementations."""

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 RecoveryStrategyBase, RecoveryContext, RecoveryPriority

logger = get_logger(__name__)


class FallbackStrategy(RecoveryStrategyBase):
    """Fallback to alternative service providers or cached responses."""
    
    def __init__(self, fallback_providers: List[Callable], cache_provider: Optional[Callable] = None):
        super().__init__("fallback", RecoveryPriority.HIGH)
        self.fallback_providers = fallback_providers
        self.cache_provider = cache_provider
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if fallback providers are available."""
        return bool(self.fallback_providers or self.cache_provider)
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute fallback strategy."""
        self.logger.info(f"Executing fallback strategy for {context.operation_id}")
        
        # Try cache first if available
        if self.cache_provider:
            try:
                result = await self.cache_provider()
                if result is not None:
                    self.logger.info(f"Fallback to cache successful for {context.operation_id}")
                    return result
            except Exception as e:
                self.logger.warning(f"Cache fallback failed: {e}")
        
        # Try alternative providers
        for i, provider in enumerate(self.fallback_providers):
            try:
                self.logger.info(f"Trying fallback provider {i+1}/{len(self.fallback_providers)}")
                result = await provider()
                self.logger.info(f"Fallback provider {i+1} successful for {context.operation_id}")
                return result
            except Exception as e:
                self.logger.warning(f"Fallback provider {i+1} failed: {e}")
        
        raise TraxError(
            f"All fallback providers failed for {context.operation_id}",
            error_code=ErrorCode.RECOVERY_FAILED
        )


class GracefulDegradationStrategy(RecoveryStrategyBase):
    """Reduce functionality when services are unavailable."""
    
    def __init__(self, degraded_operations: Dict[str, Callable]):
        super().__init__("graceful_degradation", RecoveryPriority.MEDIUM)
        self.degraded_operations = degraded_operations
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if degraded operation is available."""
        return context.operation_id in self.degraded_operations
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute degraded operation."""
        self.logger.info(f"Executing graceful degradation for {context.operation_id}")
        
        degraded_op = self.degraded_operations.get(context.operation_id)
        if not degraded_op:
            raise TraxError(
                f"No degraded operation available for {context.operation_id}",
                error_code=ErrorCode.RECOVERY_FAILED
            )
        
        try:
            result = await degraded_op()
            self.logger.info(f"Graceful degradation successful for {context.operation_id}")
            return result
        except Exception as e:
            self.logger.error(f"Graceful degradation failed: {e}")
            raise


class StateRecoveryStrategy(RecoveryStrategyBase):
    """Recover from interrupted operations using saved state."""
    
    def __init__(self, state_storage: Callable, state_loader: Callable):
        super().__init__("state_recovery", RecoveryPriority.HIGH)
        self.state_storage = state_storage
        self.state_loader = state_loader
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if state recovery is possible."""
        try:
            state = await self.state_loader(context.operation_id)
            return state is not None
        except Exception:
            return False
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute state recovery."""
        self.logger.info(f"Executing state recovery for {context.operation_id}")
        
        try:
            state = await self.state_loader(context.operation_id)
            if state is None:
                raise TraxError(
                    f"No saved state found for {context.operation_id}",
                    error_code=ErrorCode.RECOVERY_FAILED
                )
            
            # Resume operation from saved state
            result = await self._resume_from_state(state, context)
            self.logger.info(f"State recovery successful for {context.operation_id}")
            return result
        except Exception as e:
            self.logger.error(f"State recovery failed: {e}")
            raise
    
    async def _resume_from_state(self, state: Dict[str, Any], context: RecoveryContext) -> Any:
        """Resume operation from saved state."""
        # This is a placeholder - actual implementation would depend on the operation type
        return state.get("result")


class TransactionRollbackStrategy(RecoveryStrategyBase):
    """Rollback database transactions on failure."""
    
    def __init__(self, rollback_handler: Callable):
        super().__init__("transaction_rollback", RecoveryPriority.CRITICAL)
        self.rollback_handler = rollback_handler
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if transaction rollback is possible."""
        return context.metadata.get("has_transaction", False)
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute transaction rollback."""
        self.logger.info(f"Executing transaction rollback for {context.operation_id}")
        
        try:
            await self.rollback_handler(context.operation_id)
            self.logger.info(f"Transaction rollback successful for {context.operation_id}")
            return None
        except Exception as e:
            self.logger.error(f"Transaction rollback failed: {e}")
            raise


class ResourceCleanupStrategy(RecoveryStrategyBase):
    """Clean up temporary resources on failure."""
    
    def __init__(self, cleanup_handlers: List[Callable]):
        super().__init__("resource_cleanup", RecoveryPriority.MEDIUM)
        self.cleanup_handlers = cleanup_handlers
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if cleanup is needed."""
        return bool(self.cleanup_handlers)
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute resource cleanup."""
        self.logger.info(f"Executing resource cleanup for {context.operation_id}")
        
        cleanup_errors = []
        for handler in self.cleanup_handlers:
            try:
                await handler(context.operation_id)
            except Exception as e:
                cleanup_errors.append(str(e))
                self.logger.warning(f"Cleanup handler failed: {e}")
        
        if cleanup_errors:
            self.logger.warning(f"Some cleanup handlers failed: {cleanup_errors}")
        
        self.logger.info(f"Resource cleanup completed for {context.operation_id}")
        return None


class HealthCheckStrategy(RecoveryStrategyBase):
    """Proactive health checking and recovery."""
    
    def __init__(self, health_checkers: Dict[str, Callable], recovery_actions: Dict[str, Callable]):
        super().__init__("health_check", RecoveryPriority.LOW)
        self.health_checkers = health_checkers
        self.recovery_actions = recovery_actions
    
    async def can_recover(self, context: RecoveryContext) -> bool:
        """Check if health check is applicable."""
        return context.operation_id in self.health_checkers
    
    async def execute(self, context: RecoveryContext) -> Any:
        """Execute health check and recovery."""
        self.logger.info(f"Executing health check for {context.operation_id}")
        
        try:
            # Perform health check
            is_healthy = await self.health_checkers[context.operation_id]()
            
            if not is_healthy:
                # Execute recovery action
                recovery_action = self.recovery_actions.get(context.operation_id)
                if recovery_action:
                    await recovery_action()
                    self.logger.info(f"Health check recovery action executed for {context.operation_id}")
            
            return is_healthy
        except Exception as e:
            self.logger.error(f"Health check failed: {e}")
            raise


# Convenience functions for common recovery scenarios
async def create_fallback_strategy(
    primary_operation: Callable,
    fallback_operations: List[Callable],
    cache_operation: Optional[Callable] = None
) -> FallbackStrategy:
    """Create a fallback strategy with primary and backup operations."""
    return FallbackStrategy(fallback_operations, cache_operation)


async def create_graceful_degradation_strategy(
    full_operation: Callable,
    degraded_operation: Callable
) -> GracefulDegradationStrategy:
    """Create a graceful degradation strategy."""
    return GracefulDegradationStrategy({
        "full": full_operation,
        "degraded": degraded_operation
    })


async def create_state_recovery_strategy(
    state_saver: Callable,
    state_loader: Callable
) -> StateRecoveryStrategy:
    """Create a state recovery strategy."""
    return StateRecoveryStrategy(state_saver, state_loader)


async def create_transaction_rollback_strategy(
    rollback_handler: Callable
) -> TransactionRollbackStrategy:
    """Create a transaction rollback strategy."""
    return TransactionRollbackStrategy(rollback_handler)


async def create_resource_cleanup_strategy(
    cleanup_handlers: List[Callable]
) -> ResourceCleanupStrategy:
    """Create a resource cleanup strategy."""
    return ResourceCleanupStrategy(cleanup_handlers)


async def create_health_check_strategy(
    health_checker: Callable,
    recovery_action: Callable
) -> HealthCheckStrategy:
    """Create a health check strategy."""
    return HealthCheckStrategy(
        {"default": health_checker},
        {"default": recovery_action}
    )
