from __future__ import annotations

import json
from datetime import datetime, timezone
from typing import Any, Literal
from urllib.parse import urlparse

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator


def utc_now() -> datetime:
    return datetime.now(timezone.utc)


class StrictModel(BaseModel):
    model_config = ConfigDict(extra="forbid")


class MediaJobRequest(StrictModel):
    kind: Literal["video", "image"]
    prompt: str = Field(min_length=1, max_length=32_000)
    session_id: str = Field(min_length=1, max_length=160)
    turn_id: str = Field(min_length=1, max_length=160)
    trace_id: str = Field(min_length=1, max_length=160)
    options: dict[str, Any] = Field(default_factory=dict)

    @model_validator(mode="after")
    def options_are_bounded(self) -> "MediaJobRequest":
        # The runtime applies the configured bound as well; this absolute cap
        # prevents programmatic callers from bypassing the HTTP boundary.
        if len(json.dumps(self.options, sort_keys=True, separators=(",", ":"))) > 32 * 1024:
            raise ValueError("options exceed the absolute size limit")
        return self


class MediaArtifact(StrictModel):
    artifact_id: str = Field(min_length=1, max_length=256)
    url: str = Field(min_length=1, max_length=4096)
    mime_type: str = Field(
        min_length=3,
        max_length=160,
        pattern=r"^[a-z0-9.+-]+/[a-z0-9.+-]+$",
    )

    @field_validator("url")
    @classmethod
    def reference_only(cls, value: str) -> str:
        parsed = urlparse(value)
        if parsed.scheme.casefold() == "data" or "base64," in value.casefold():
            raise ValueError("embedded media payloads are forbidden")
        if parsed.username or parsed.password or parsed.fragment:
            raise ValueError("artifact references must not contain credentials or fragments")
        if value.startswith("//") or "\\" in value:
            raise ValueError("protocol-relative and backslash artifact paths are forbidden")
        if parsed.scheme not in {"http", "https"} and not value.startswith("/"):
            raise ValueError("artifact must be an HTTP(S) URL or local absolute path")
        return value


JobStatus = Literal[
    "created", "queued", "started", "cancelling", "completed", "failed", "cancelled"
]


class MediaJobView(StrictModel):
    job_id: str = Field(min_length=1, max_length=160)
    kind: Literal["video", "image"]
    status: JobStatus
    progress: float | None = Field(default=None, ge=0, le=1)
    created_at: datetime
    updated_at: datetime
    artifacts: list[MediaArtifact] = Field(default_factory=list)
    error: dict[str, str] | None = None


class JobAccepted(StrictModel):
    job_id: str
    replayed: bool = False


class StoredEvent(StrictModel):
    seq: int = Field(ge=1)
    event_id: str
    type: str
    payload: dict[str, Any]
    timestamp: datetime = Field(default_factory=utc_now)

    def sse(self) -> str:
        data = json.dumps(self.payload, ensure_ascii=False, separators=(",", ":"))
        return f"id: {self.event_id}\nevent: {self.type}\ndata: {data}\n\n"
