import uuid
from enum import Enum
from typing import List, Union, Optional, Dict, Any

import pydantic
from pydantic import BaseModel, Field, field_validator, ConfigDict, HttpUrl, computed_field
from .ffmpeg_worker_model import FFMpegSliceSegment
from .media_model import MediaSource, MediaSources, MediaProtocol
from ..config import WorkerConfig
from ..utils.VideoUtils import VideoMetadata

config = WorkerConfig()


class TaskStatus(str, Enum):
    running = "running"
    failed = "failed"
    success = "success"
    expired = "expired"


class ErrorCode(int, Enum):
    SUCCESS = 0
    PARAM_ERROR = 10001
    NOT_FOUND = 10002
    UNAUTHORIZED = 10003
    FORBIDDEN = 10004
    BUSINESS_ERROR = 10005
    SYSTEM_ERROR = 99999


class WebhookMethodEnum(str, Enum):
    GET = "get"
    POST = "post"


class SentryTransactionHeader(BaseModel):
    x_trace_id: Optional[str] = Field(description="Sentry Transaction ID", default=None)
    x_baggage: Optional[str] = Field(description="Sentry Transaction baggage", default=None)


class SentryTransactionInfo(BaseModel):
    x_trace_id: str = Field(description="Sentry Transaction ID")
    x_baggage: str = Field(description="Sentry Transaction baggage")


class FFMPEGSliceTaskStatusRequest(BaseModel):
    taskId: str = Field(description="任务Id")


class ModalTaskResponse(BaseModel):
    success: bool = Field(description="任务接受成功")
    taskId: str = Field(description="任务Id")


class RecordingTaskResponse(BaseModel):
    success: bool = Field(description="任务接受成功")
    taskId: str = Field(description="任务Id")
    manifest: str = Field(description="播放地址")
    manifest_urn: str = Field(description="播放列表URN")


class WebhookNotify(BaseModel):
    endpoint: HttpUrl = Field(description="Webhook回调端点", examples=["https://webhook.example.com?query=123"])
    method: WebhookMethodEnum = Field(
        description="Webhook回调请求方法, 使用POST方法时body与查询ffmpeg任务所获得的json body格式一致")
    headers: Optional[Dict[str, str]] = Field(description="Webhook回调附带的Headers", default=None)

    model_config = ConfigDict(json_schema_extra={
        "description": "Webhook返回值与查询ffmpeg任务所获得的json格式一致"
    })


class FFMPEGResult(BaseModel):
    urn: str = Field(description="FFMPEG任务结果urn")
    content_length: int = Field(description="媒体资源文件字节大小(Byte)")
    metadata: VideoMetadata = Field(description="媒体元数据")

    @computed_field(description="可通过CDN访问的资源链接")
    @property
    def url(self) -> str:
        if not self.urn.startswith("s3://"):
            raise ValueError("无法转换非s3前缀协议")
        return self.urn.replace('s3://', f"{config.S3_cdn_endpoint}/")


class BaseFFMPEGTaskRequest(BaseModel):
    webhook: Optional[WebhookNotify] = Field(description="Task webhook", default=None)


class BaseFFMPEGTaskStatusResponse(BaseModel):
    taskId: str = Field(description="任务Id")
    status: TaskStatus = Field(description="任务运行状态")
    error: Optional[str] = Field(description="任务错误原因", default=None)
    code: Optional[int] = Field(description="任务错误原因代码", default=None)
    task_type: str = Field(description="任务类型", default="unknown")
    results: Optional[List[Union[FFMPEGResult, Any]]] = Field(description="任务运行结果", default=None)

    model_config = ConfigDict(extra='ignore')


class FFMPEGConvertStreamRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="待转换的媒体源")

    @field_validator('media', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            media_source = MediaSource.from_str(v)
            if media_source.protocol == MediaProtocol.hls:
                return media_source
            else:
                raise pydantic.ValidationError('media只支持hls格式的urn')
        elif isinstance(v, MediaSource):
            return v
        else:
            raise pydantic.ValidationError("media格式读取失败")


class FFMPEGConvertStreamResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="任务运行结果")


class FFMPEGSliceRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="待切割的媒体源")
    markers: List[FFMpegSliceSegment] = Field(description="按照时间顺序排序过的切割标记数组")

    @field_validator('media', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGSliceTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[List[str]] = Field(default=None, description="任务运行结果")


class FFMPEGConcatRequest(BaseFFMPEGTaskRequest):
    medias: MediaSources = Field(description="待合并的媒体源")


class FFMPEGConcatTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="任务运行结果")


class FFMPEGExtractAudioRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="待提取音频的媒体源")

    @field_validator('media', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGExtractAudioTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="任务运行结果")


class FFMPEGCornerMirrorRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")

    @field_validator('media', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGCornerMirrorTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="任务运行结果")


class FFMPEGBgmRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")
    bgm_media: MediaSource = Field(description="添加的BGM媒体源", alias="bgmMedia")

    @field_validator('media', mode='before')
    @classmethod
    def parse_media_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)

    @field_validator('bgm_media', mode='before')
    @classmethod
    def parse_bgm_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            media = MediaSource.from_str(v)
            if not media.file_extension in ['wav', 'mp3']:
                raise pydantic.ValidationError("必须使用符合规范的音频格式, 如wav或者mp3")
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGBgmTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGZoomLoopRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")
    duration: float = Field(description="放大缩小一个循环的持续时间秒数", default=6.0)
    zoom: float = Field(description="放大缩小系数", default=0.1)

    @field_validator('media', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGZoomLoopTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGOverlayGifRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")
    gif: MediaSource = Field(description="叠加的特效gif")

    @field_validator('media', mode='before')
    @classmethod
    def parse_media_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(f"解析{type(v)}类型不支持")

    @field_validator('gif', mode='before')
    @classmethod
    def parse_fig_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            media = MediaSource.from_str(v)
            if not media.file_extension == 'gif':
                raise pydantic.ValidationError("必须使用.gif文件")
            return media
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGOverlayGifTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGSubtitleOverlayRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")
    subtitle: MediaSource = Field(description="需要叠加的字幕文件")
    fonts: List[MediaSource] = Field(description="字幕文件内使用到的字体文件")

    @field_validator('media', mode='before')
    @classmethod
    def parse_media_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)

    @field_validator('subtitle', mode='before')
    @classmethod
    def parse_subtitle_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            media = MediaSource.from_str(v)
            if not media.file_extension in ['ass']:
                raise pydantic.ValidationError("必须使用标准字幕文件, 如.ass")
            return media
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)

    @field_validator('fonts', mode='before')
    @classmethod
    def parse_font_input(cls, v: Union[str, MediaSource]) -> List[MediaSource]:
        if not v:
            raise pydantic.ValidationError("fonts输入为空")
        result = []
        for item in v:
            if isinstance(item, str):
                result.append(MediaSource.from_str(item))
            elif isinstance(item, MediaSource):
                result.append(item)
            else:
                raise pydantic.ValidationError("fonts元素类型错误: 必须是字符串")
        return result


class FFMPEGSubtitleTaskStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGMixBgmWithNoiseReduceRequest(BaseFFMPEGTaskRequest):
    media: MediaSource = Field(description="需要处理的媒体源")
    bgm: MediaSource = Field(description="需要添加的BGM媒体源")
    video_volume: float = Field(description="最终输出的视频音量系数", default=1.4)
    music_volume: float = Field(description="最终输出的BGM音量系数", default=0.1)
    noise_sample: Optional[MediaSource] = Field(description="常考噪音样本，如不指定将使用源视频的前2秒作为样本",
                                                default=None)

    @field_validator('media', mode='before')
    @classmethod
    def parse_media_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)

    @field_validator('bgm', mode='before')
    @classmethod
    def parse_bgm_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)

    @field_validator('noise_sample', mode='before')
    @classmethod
    def parse_sample_input(cls, v: Union[None, str, MediaSource]):
        if not v:
            return None
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGMixBgmWithNoiseReduceStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGVideoLoopFillAudioRequest(BaseFFMPEGTaskRequest):
    video: MediaSource = Field(description="用来填充的视频素材")
    audio: MediaSource = Field(description="被填充的音频素材")


class FFMPEGVideoLoopFillAudioResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class FFMPEGExtractFrameRequest(BaseFFMPEGTaskRequest):
    video: MediaSource = Field(description="提取帧画面的来源")
    frame_index: int = Field(description="提取的第几帧, 从1开始，默认为1", default=1)

    @field_validator('video', mode='before')
    @classmethod
    def parse_video_input(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGExtractFrameStatusResponse(BaseFFMPEGTaskStatusResponse):
    result: Optional[str] = Field(default=None, description="生成结果的URN")


class ModalTaskCancelResponse(BaseModel):
    success: bool = Field(description="成功取消任务")
    error: Optional[str] = Field(default=None, description="失败原因")


class ComfyTaskStatusResponse(BaseModel):
    taskId: str = Field(description="任务Id")
    status: TaskStatus = Field(description="任务运行状态")
    error: Optional[str] = Field(description="任务错误原因", default=None)
    code: Optional[int] = Field(description="任务错误原因代码", default=None)
    result: Optional[str] = Field(description="任务运行结果", default=None)


class ComfyTaskRequest(BaseFFMPEGTaskRequest):
    video_path: MediaSource = Field(
        default=None, description="视频源")
    start_time: str = Field(default="00:00:01.600", description="开始时间")
    filename_prefix: str = Field(default=str(uuid.uuid4()), description="生成文件名前缀")
    tts_text1: str = Field(default="好好看，这是我们家专门为女生定制的背心，", description="tts文本1")
    tts_text2: str = Field(default="", description="tts文本2")
    tts_text3: str = Field(default="", description="tts文本3")
    tts_text4: str = Field(default="", description="tts文本4")
    anchor_id: str = Field(default="dawan", description="讲话人ID")
    speed: float = Field(default=1, description="讲话语速")

    @field_validator('video_path', mode='before')
    @classmethod
    def parse_inputs(cls, v: Union[str, MediaSource]):
        if isinstance(v, str):
            return MediaSource.from_str(v)
        elif isinstance(v, MediaSource):
            return v
        else:
            raise TypeError(v)


class FFMPEGStreamRecordRequest(BaseFFMPEGTaskRequest):
    stream_source: str = Field(description="直播源地址")
    segment_duration: int = Field(default=5, description="hls片段时长(秒)")
    recording_timeout: int = Field(default=300, description="hls流无内容后等待的时长(秒)")
    monitor_timeout: int = Field(default=36000, description="录制监控最大时长(秒), 默认为10小时, 不可大于12小时", le=43200)


class GeminiRequest(BaseFFMPEGTaskRequest):
    media_hls_url: str = Field(default="", description="视频流录制HLS地址 hls://格式 需录制超过20分钟")
    product_list: List[str] = Field(description="商品名列表(时间倒序)"),
    start_time: str = Field(default="00:00:00.000", description="开始时间(hls)")
    end_time:str = Field(default="00:20:00.000", description="结束时间(hls)")

class GeminiResultResponse(BaseFFMPEGTaskStatusResponse):
    result: str = Field(default="", description="推理出的json")

class LiveProduct(BaseModel):
    title: str = Field(default="", description="商品标题")
    leaf_category: str = Field(default="", description="商品分类")
    shop_id: int = Field(default=0, description="店铺ID")
    product_id: str = Field(default="", description="商品ID")
    cover: str = Field(default="", description="商品封面图链接")
    detail_url: str = Field(default="", description="商品详情链接")

class LiveProductCaches(BaseModel):
    room_id: str = Field(default="", description="直播间room_id/Room room_id")
    author_id: str = Field(default="", description="作者id/Author id")
    update_time: str = Field(default=0, description="商品列表更新时间")
    count: int = Field(default=0, description="缓存直播间商品数量")
    product_list: List[LiveProduct] = Field(default=None,description="缓存直播间商品列表")

class MonitorLiveRoomProductRequest(BaseModel):
    cookie: str = Field(default="YOUR_COOKIE", description="用户网页版抖音Cookie/Your web version of Douyin Cookie")
    room_id: str = Field(default="", description="直播间room_id/Room room_id")
    author_id: str = Field(default="", description="作者id/Author id")

class LiveRoomProductCachesResponse(BaseModel):
    status: int = Field(default=None, description="缓存状态/0-正常返回 1-直播已结束 2-IP风控 3-请求Tikhub API错误 4-内部错误")
    message: str = Field(default="", description="错误信息")
    cache_json: str = Field(default="", description="缓存内容/Json文本")