import uuid
from typing import Any, Literal

from pydantic import BaseModel, Field, field_validator, model_validator


class StartSessionInput(BaseModel):
    workspace_id: uuid.UUID
    session_id: uuid.UUID
    profile_id: uuid.UUID
    profile_storage_key: str = Field(pattern=r"^[a-f0-9]{32}$")
    fencing_token: int = Field(gt=0)


class CommandInput(BaseModel):
    fencing_token: int = Field(gt=0)


class BrowserActionInput(BaseModel):
    fencing_token: int = Field(gt=0)
    tool: Literal[
        "observe", "navigate", "click", "type", "press", "wait_for", "extract",
        "tab_list", "tab_open", "tab_switch", "tab_close", "screenshot",
    ]
    arguments: dict[str, Any] = Field(default_factory=dict)

    @model_validator(mode="after")
    def validate_arguments_size(self):
        if len(self.arguments) > 20:
            raise ValueError("Too many tool arguments")
        return self


class HumanPointerInput(BaseModel):
    fencing_token: int = Field(gt=0)
    action: Literal["click", "move", "scroll"]
    x: float = Field(ge=0, le=10_000)
    y: float = Field(ge=0, le=10_000)
    delta_x: float = Field(default=0, ge=-10_000, le=10_000)
    delta_y: float = Field(default=0, ge=-10_000, le=10_000)


class HumanKeyboardInput(BaseModel):
    fencing_token: int = Field(gt=0)
    text: str | None = Field(default=None, max_length=20_000)
    key: str | None = Field(default=None, min_length=1, max_length=80)

    @model_validator(mode="after")
    def require_one_input(self):
        if (self.text is None) == (self.key is None):
            raise ValueError("Provide either text or key")
        return self


class SessionState(BaseModel):
    worker_id: str
    status: Literal["ready", "ai_controlled", "human_controlled", "paused", "stopped"]
    current_url: str | None = Field(default=None, max_length=4096)
    active_tab_id: str | None = None
    tabs: list[dict[str, Any]] = Field(default_factory=list)
    page_title: str | None = Field(default=None, max_length=500)
    observed_at: str | None = None


class ActionResult(BaseModel):
    session: SessionState
    tool: str
    result: dict[str, Any]
    verification: dict[str, Any]


class StreamFrame(BaseModel):
    session_id: uuid.UUID
    fencing_token: int = Field(gt=0)
    image_base64: str
    mime_type: Literal["image/jpeg"] = "image/jpeg"
    width: int
    height: int

    @field_validator("image_base64")
    @classmethod
    def limit_frame(cls, value: str) -> str:
        if len(value) > 5_000_000:
            raise ValueError("Frame is too large")
        return value
