"""
Generation Parameters - 统一的参数定义和处理

本模块定义了生成任务的标准参数结构，明确参数职责划分：
- MetadataParams: 元数据参数（用于路由和追踪，不传给 Provider）
- GenerationParams: 生成参数（传给 Provider）
- ProviderParams: Provider 特定参数（由 Adapter 转换）
"""

from typing import List, Optional, Dict, Any, Literal
from pydantic import BaseModel, Field, field_validator, ConfigDict
import re


# ============================================================================
# 参数职责划分
# ============================================================================

class MetadataParams(BaseModel):
    """元数据参数 - 用于路由、追踪和管理，不传递给 Provider
    
    这些参数在 TaskManager 层会被移除，不会传递给 Provider API
    """
    model: str = Field(..., description="模型 ID")
    provider: str = Field(..., description="Provider ID")
    project_id: Optional[str] = Field(None, alias="projectId", description="项目 ID")
    source: Optional[str] = Field(None, description="来源类型 (e.g., 'storyboard', 'asset')")
    source_id: Optional[str] = Field(None, alias="sourceId", description="来源 ID")

    model_config = ConfigDict(populate_by_name=True)


class MediaInputs(BaseModel):
    """媒体输入参数 - 统一命名规范
    
    统一使用复数形式的数组，避免多种命名方式
    """
    image_inputs: Optional[List[str]] = Field(None, alias="imageInputs", description="输入图片 URL 列表")
    video_inputs: Optional[List[str]] = Field(None, alias="videoInputs", description="输入视频 URL 列表")
    audio_inputs: Optional[List[str]] = Field(None, alias="audioInputs", description="输入音频 URL 列表")

    model_config = ConfigDict(populate_by_name=True)

    def get_first_image(self) -> Optional[str]:
        """获取首帧图片"""
        return self.image_inputs[0] if self.image_inputs else None
    
    def get_last_image(self) -> Optional[str]:
        """获取尾帧图片（用于 KF2V）"""
        return self.image_inputs[1] if self.image_inputs and len(self.image_inputs) > 1 else None
    
    def get_first_video(self) -> Optional[str]:
        """获取首个视频"""
        return self.video_inputs[0] if self.video_inputs else None
    
    def get_first_audio(self) -> Optional[str]:
        """获取首个音频"""
        return self.audio_inputs[0] if self.audio_inputs else None


class DimensionParams(BaseModel):
    """尺寸参数 - 统一的尺寸定义
    
    优先级: size > aspect_ratio + resolution
    """
    size: Optional[str] = Field(None, description="分辨率字符串 (e.g., '1280*720')")
    aspect_ratio: Optional[str] = Field(None, alias="aspectRatio", description="宽高比 (e.g., '16:9')")
    resolution: Optional[str] = Field(None, description="分辨率等级 (图片: '1024x1024', 视频: '720P')")

    model_config = ConfigDict(populate_by_name=True)

    @field_validator('aspect_ratio')
    @classmethod
    def validate_aspect_ratio(cls, v: Optional[str]) -> Optional[str]:
        if v is None:
            return v
        if not re.match(r'^\d+:\d+$', v):
            raise ValueError(f"Invalid aspect ratio format: {v}. Expected 'width:height' (e.g., '16:9')")
        return v


class ImageGenerationParams(BaseModel):
    """图片生成参数 - 传递给 Provider 的标准参数
    
    这些参数会被传递给 Provider Service 和 Adapter
    """
    # 必需参数
    prompt: str = Field(..., description="提示词")
    
    # 可选参数
    negative_prompt: Optional[str] = Field(None, alias="negativePrompt", description="反向提示词")
    
    # 媒体输入
    media: MediaInputs = Field(default_factory=MediaInputs, description="媒体输入")
    
    # 尺寸参数
    dimensions: DimensionParams = Field(default_factory=DimensionParams, description="尺寸参数")
    
    # 生成控制
    n: int = Field(1, ge=1, le=10, description="生成数量")
    
    # 高级参数
    lora_strength: Optional[float] = Field(None, alias="loraStrength", ge=0.0, le=1.0, description="LoRA 强度")
    guidance_scale: Optional[float] = Field(None, alias="guidanceScale", description="引导强度")
    steps: Optional[int] = Field(None, description="推理步数")
    
    # 扩展参数
    extra_params: Optional[Dict[str, Any]] = Field(None, alias="extraParams", description="额外参数")

    model_config = ConfigDict(populate_by_name=True)

    @field_validator('prompt')
    @classmethod
    def validate_prompt(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Prompt cannot be empty")
        return v.strip()

    def to_provider_dict(self) -> Dict[str, Any]:
        """转换为 Provider 可用的字典格式

        扁平化结构，移除嵌套
        """
        result = {
            "prompt": self.prompt,
            "negative_prompt": self.negative_prompt,
            "n": self.n,
            "lora_strength": self.lora_strength,
            "guidance_scale": self.guidance_scale,
            "steps": self.steps,
        }
        
        # 添加媒体输入
        if self.media.image_inputs:
            result["image_inputs"] = self.media.image_inputs
        if self.media.video_inputs:
            result["video_inputs"] = self.media.video_inputs
        if self.media.audio_inputs:
            result["audio_inputs"] = self.media.audio_inputs
        
        # 添加尺寸参数
        if self.dimensions.size:
            result["size"] = self.dimensions.size
        if self.dimensions.aspect_ratio:
            result["aspect_ratio"] = self.dimensions.aspect_ratio
        if self.dimensions.resolution:
            result["resolution"] = self.dimensions.resolution
        
        # 合并额外参数
        if self.extra_params:
            for key, value in self.extra_params.items():
                if value is not None:
                    result[key] = value
        
        # 移除 None 值
        return {k: v for k, v in result.items() if v is not None}


class VideoGenerationParams(BaseModel):
    """视频生成参数 - 传递给 Provider 的标准参数"""
    # 必需参数
    prompt: str = Field(..., description="提示词")
    
    # 可选参数
    negative_prompt: Optional[str] = Field(None, alias="negativePrompt", description="反向提示词")
    
    # 媒体输入
    media: MediaInputs = Field(default_factory=MediaInputs, description="媒体输入")
    
    # 尺寸参数
    dimensions: DimensionParams = Field(default_factory=DimensionParams, description="尺寸参数")
    
    # 视频特定参数
    duration: int = Field(5, ge=1, le=60, description="视频时长（秒）")
    enable_audio: Optional[bool] = Field(True, alias="enableAudio", description="是否生成有声视频")
    n: int = Field(1, ge=1, le=10, description="生成数量")
    
    # 高级控制
    camera_control: Optional[str] = Field(None, alias="cameraControl", description="相机控制类型")
    shot_type: Optional[str] = Field(None, alias="shotType", description="镜头类型")
    
    # 扩展参数
    extra_params: Optional[Dict[str, Any]] = Field(None, alias="extraParams", description="额外参数")

    model_config = ConfigDict(populate_by_name=True)

    @field_validator('prompt')
    @classmethod
    def validate_prompt(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Prompt cannot be empty")
        return v.strip()

    def to_provider_dict(self) -> Dict[str, Any]:
        """转换为 Provider 可用的字典格式"""
        result = {
            "prompt": self.prompt,
            "negative_prompt": self.negative_prompt,
            "duration": self.duration,
            "enable_audio": self.enable_audio,
            "n": self.n,
            "camera_control": self.camera_control,
            "shot_type": self.shot_type,
        }
        
        # 添加媒体输入
        if self.media.image_inputs:
            result["image_inputs"] = self.media.image_inputs
        if self.media.video_inputs:
            result["video_inputs"] = self.media.video_inputs
        if self.media.audio_inputs:
            result["audio_inputs"] = self.media.audio_inputs
        
        # 添加尺寸参数
        if self.dimensions.size:
            result["size"] = self.dimensions.size
        if self.dimensions.aspect_ratio:
            result["aspect_ratio"] = self.dimensions.aspect_ratio
        if self.dimensions.resolution:
            result["resolution"] = self.dimensions.resolution
        
        # 合并额外参数
        if self.extra_params:
            for key, value in self.extra_params.items():
                if value is not None:
                    result[key] = value
        
        # 移除 None 值
        return {k: v for k, v in result.items() if v is not None}


# ============================================================================
# 参数转换工具
# ============================================================================

class ParameterConverter:
    """参数转换工具 - 统一的参数转换逻辑"""
    
    @staticmethod
    def from_request_to_generation_params(
        request_dict: Dict[str, Any],
        task_type: Literal["image", "video"]
    ) -> tuple[MetadataParams, Any]:
        """从请求字典转换为标准参数
        
        Args:
            request_dict: 请求参数字典
            task_type: 任务类型
        
        Returns:
            (metadata_params, generation_params)
        """
        # 提取元数据
        metadata = MetadataParams(
            model=request_dict.get("model"),
            provider=request_dict.get("provider"),
            project_id=request_dict.get("project_id"),
            source=request_dict.get("source"),
            source_id=request_dict.get("source_id")
        )
        
        # 构建媒体输入
        media = MediaInputs(
            image_inputs=request_dict.get("image_inputs"),
            video_inputs=request_dict.get("video_inputs"),
            audio_inputs=request_dict.get("audio_inputs")
        )
        
        # 构建尺寸参数
        dimensions = DimensionParams(
            size=request_dict.get("size"),
            aspect_ratio=request_dict.get("aspect_ratio"),
            resolution=request_dict.get("resolution")
        )
        
        # 根据任务类型构建生成参数
        if task_type == "image":
            generation_params = ImageGenerationParams(
                prompt=request_dict.get("prompt"),
                negative_prompt=request_dict.get("negative_prompt"),
                media=media,
                dimensions=dimensions,
                n=request_dict.get("n", 1),
                lora_strength=request_dict.get("lora_strength"),
                guidance_scale=request_dict.get("guidance_scale"),
                steps=request_dict.get("steps"),
                extra_params=request_dict.get("extra_params")
            )
        else:  # video
            generation_params = VideoGenerationParams(
                prompt=request_dict.get("prompt"),
                negative_prompt=request_dict.get("negative_prompt"),
                media=media,
                dimensions=dimensions,
                duration=request_dict.get("duration", 5),
                enable_audio=request_dict.get("enable_audio", True),
                n=request_dict.get("n", 1),
                camera_control=request_dict.get("camera_control"),
                shot_type=request_dict.get("shot_type"),
                extra_params=request_dict.get("extra_params")
            )
        
        return metadata, generation_params
    
    @staticmethod
    def prepare_provider_params(
        generation_params: Any,
        normalize_media: bool = True
    ) -> Dict[str, Any]:
        """准备传递给 Provider 的参数
        
        Args:
            generation_params: ImageGenerationParams 或 VideoGenerationParams
            normalize_media: 是否规范化媒体参数（为不同 Provider 提供别名）
        
        Returns:
            Provider 可用的参数字典
        """
        # 转换为字典
        params = generation_params.to_provider_dict()
        
        # 规范化媒体参数（为不同 Provider 提供别名）
        if normalize_media:
            # 图片参数别名
            if "image_inputs" in params and params["image_inputs"]:
                params["image_urls"] = params["image_inputs"]  # 别名
                params["image"] = params["image_inputs"][0]  # 首帧
                params["first_frame_image"] = params["image_inputs"][0]  # MiniMax
                params["first_frame_url"] = params["image_inputs"][0]  # DashScope
                
                if len(params["image_inputs"]) > 1:
                    params["image_tail"] = params["image_inputs"][1]  # 尾帧
                    params["last_frame_url"] = params["image_inputs"][1]  # DashScope KF2V
            
            # 视频参数别名
            if "video_inputs" in params and params["video_inputs"]:
                params["video_urls"] = params["video_inputs"]
                params["video"] = params["video_inputs"][0]
                params["ref_video_url"] = params["video_inputs"][0]  # DashScope
            
            # 音频参数别名
            if "audio_inputs" in params and params["audio_inputs"]:
                params["audio_urls"] = params["audio_inputs"]
                params["audio"] = params["audio_inputs"][0]
                params["audio_url"] = params["audio_inputs"][0]
        
        return params
