""" 依赖 Injection for Service Registry
Provides FastAPI dependencies for clean service injection
"""
from typing import Optional, Callable
from fastapi import Depends, Header
from src.services.provider.registry import ModelRegistry, ModelType
from src.services.provider.base import (
    BaseLLMService, 
    BaseImageService, 
    BaseVideoService, 
    BaseAudioService
)
from src.services.provider.health import health_monitor
from src.utils.errors import ModelNotFoundException, ModelNotAvailableException
import logging

logger = logging.getLogger(__name__)


# ============================================================================
# 服务 Provider Dependencies
# ============================================================================

def get_llm_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID")
) -> BaseLLMService:
    """ 依赖 to get LLM service.
    
    Args:
        model_id: Model ID from query parameter
        x_model_id: Model ID from header
        
    Returns:
        BaseLLMService instance
        
    Raises:
        ModelNotFoundException: If service not found
        ModelNotAvailableException: If service unavailable
    """
    # 优先级: query param > header > default
    service_id = model_id or x_model_id
    
    if service_id:
        service = ModelRegistry.get(service_id)
        if not service:
            raise ModelNotFoundException(service_id)
    else:
        service = ModelRegistry.get_default(ModelType.LLM)
        if not service:
            raise ModelNotAvailableException("default-llm", "No default LLM service configured")
    
    return service


def get_image_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID"),
    require_ref_image: bool = False
) -> BaseImageService:
    """ 依赖 to get Image service.
    
    Args:
        model_id: Model ID from query parameter
        x_model_id: Model ID from header
        require_ref_image: Whether reference image support is required
        
    Returns:
        BaseImageService instance
        
    Raises:
        ModelNotFoundException: If service not found
        ModelNotAvailableException: If service unavailable
    """
    service_id = model_id or x_model_id
    
    if service_id:
        service = ModelRegistry.get(service_id)
        if not service:
            raise ModelNotFoundException(service_id)
    elif require_ref_image:
        # 查找 a service that supports reference images
        models = ModelRegistry.find_services(
            model_type=ModelType.IMAGE,
            capabilities={"supportsRefImage": True},
            enabled_only=True
        )
        if not models:
            raise ModelNotAvailableException(
                "image-ref-support",
                "No image service with reference image support available"
            )
        service = ModelRegistry.get(models[0]["id"])
    else:
        service = ModelRegistry.get_default(ModelType.IMAGE)
        if not service:
            raise ModelNotAvailableException("default-image", "No default image service configured")
    
    return service


def get_video_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID")
) -> BaseVideoService:
    """ 依赖 to get Video service.
    
    Args:
        model_id: Model ID from query parameter
        x_model_id: Model ID from header
        
    Returns:
        BaseVideoService instance
        
    Raises:
        ModelNotFoundException: If service not found
        ModelNotAvailableException: If service unavailable
    """
    service_id = model_id or x_model_id
    
    if service_id:
        service = ModelRegistry.get(service_id)
        if not service:
            raise ModelNotFoundException(service_id)
    else:
        service = ModelRegistry.get_default(ModelType.VIDEO)
        if not service:
            raise ModelNotAvailableException("default-video", "No default video service configured")
    
    return service


def get_audio_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID")
) -> BaseAudioService:
    """ 依赖 to get Audio service.
    
    Args:
        model_id: Model ID from query parameter
        x_model_id: Model ID from header
        
    Returns:
        BaseAudioService instance
        
    Raises:
        ModelNotFoundException: If service not found
        ModelNotAvailableException: If service unavailable
    """
    service_id = model_id or x_model_id
    
    if service_id:
        service = ModelRegistry.get(service_id)
        if not service:
            raise ModelNotFoundException(service_id)
    else:
        service = ModelRegistry.get_default(ModelType.AUDIO)
        if not service:
            raise ModelNotAvailableException("default-audio", "No default audio service configured")
    
    return service


# ============================================================================
# Health-Aware Service Dependencies
# ============================================================================

async def get_healthy_llm_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID"),
    allow_degraded: bool = True
) -> BaseLLMService:
    """ 获取 a healthy LLM service with automatic fallback.
    
    Args:
        model_id: Preferred model ID
        x_model_id: Model ID from header
        allow_degraded: Whether to allow degraded services
        
    Returns:
        Healthy BaseLLMService instance
        
    Raises:
        ModelNotAvailableException: If no healthy service available
    """ 
    service_id = model_id or x_model_id
    
    # preferred service first
    if service_id:
        service = ModelRegistry.get(service_id)
        if service:
            health = health_monitor.get_health(service_id)
            if health and health.status.value in ["healthy", "degraded" if allow_degraded else "healthy"]:
                return service
            logger.warning(f"Preferred service {service_id} is unhealthy, trying fallback")
    
    # 查找 healthy alternatives
    models = ModelRegistry.find_services(
        model_type=ModelType.LLM,
        enabled_only=True
    )
    
    for model in models:
        if model["id"] == service_id:
            continue  # Already tried
        
        health = health_monitor.get_health(model["id"])
        if not health or health.status.value in ["healthy", "degraded" if allow_degraded else "healthy"]:
            logger.info(f"Using fallback service: {model['id']}")
            return ModelRegistry.get(model["id"])
    
    # Last resort: use default
    service = ModelRegistry.get_default(ModelType.LLM)
    if service:
        return service
    
    raise ModelNotAvailableException("llm", "No healthy LLM service available")


async def get_healthy_image_service(
    model_id: Optional[str] = None,
    x_model_id: Optional[str] = Header(None, alias="X-Model-ID"),
    allow_degraded: bool = True,
    require_ref_image: bool = False
) -> BaseImageService:
    """ 获取 a healthy Image service with automatic fallback.
    
    Args:
        model_id: Preferred model ID
        x_model_id: Model ID from header
        allow_degraded: Whether to allow degraded services
        require_ref_image: Whether reference image support is required
        
    Returns:
        Healthy BaseImageService instance
        
    Raises:
        ModelNotAvailableException: If no healthy service available
    """ 
    service_id = model_id or x_model_id
    
    # preferred service first
    if service_id:
        service = ModelRegistry.get(service_id)
        if service:
            health = health_monitor.get_health(service_id)
            if health and health.status.value in ["healthy", "degraded" if allow_degraded else "healthy"]:
                return service
            logger.warning(f"Preferred service {service_id} is unhealthy, trying fallback")
    
    # 查找 healthy alternatives
    models = ModelRegistry.find_services(
        model_type=ModelType.IMAGE,
        capabilities={"supportsRefImage": True} if require_ref_image else None,
        enabled_only=True
    )
    
    for model in models:
        if model["id"] == service_id:
            continue
        
        health = health_monitor.get_health(model["id"])
        if not health or health.status.value in ["healthy", "degraded" if allow_degraded else "healthy"]:
            logger.info(f"Using fallback service: {model['id']}")
            return ModelRegistry.get(model["id"])
    
    # Last resort: use default
    service = ModelRegistry.get_default(ModelType.IMAGE)
    if service:
        return service
    
    raise ModelNotAvailableException("image", "No healthy image service available")


# ============================================================================
# 自定义 Service Dependency Factory
# ============================================================================

def create_service_dependency(
    model_type: ModelType,
    health_check: bool = False,
    allow_degraded: bool = True,
    required_capabilities: Optional[dict] = None
) -> Callable:
    """ 工厂 to create custom service dependencies.
    
    Args:
        model_type: Type of model service
        health_check: Whether to perform health checks
        allow_degraded: Whether to allow degraded services
        required_capabilities: Required service capabilities
        
    Returns:
        Dependency function
        
    Example:
        ```python
        get_ref_image_service = create_service_dependency(
            model_type=ModelType.IMAGE,
            health_check=True,
            required_capabilities={"supportsRefImage": True}
        )
        
        @router.post("/generate")
        async def generate(service: BaseImageService = Depends(get_ref_image_service)):
            return await service.generate(prompt)
        ```
    """ 
    async def dependency(
        model_id: Optional[str] = None,
        x_model_id: Optional[str] = Header(None, alias="X-Model-ID")
    ):
        service_id = model_id or x_model_id
        
        # specific service
        if service_id:
            service = ModelRegistry.get(service_id)
            if service:
                if health_check:
                    health = health_monitor.get_health(service_id)
                    if health and health.status.value not in ["healthy", "degraded" if allow_degraded else "healthy"]:
                        logger.warning(f"Service {service_id} is unhealthy")
                    else:
                        return service
                else:
                    return service
        
        # 查找 suitable service
        models = ModelRegistry.find_services(
            model_type=model_type,
            capabilities=required_capabilities,
            enabled_only=True
        )
        
        if health_check:
            # 过滤 by health status
            for model in models:
                if model["id"] == service_id:
                    continue
                health = health_monitor.get_health(model["id"])
                if not health or health.status.value in ["healthy", "degraded" if allow_degraded else "healthy"]:
                    return ModelRegistry.get(model["id"])
        elif models:
            return ModelRegistry.get(models[0]["id"])
        
        # Fallback to default
        service = ModelRegistry.get_default(model_type)
        if service:
            return service
        
        raise ModelNotAvailableException(model_type.value, f"No {model_type.value} service available")
    
    return dependency
