from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, ConfigDict, Field, field_validator


PROTOCOL_VERSION = "1.0"


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


class ASRStart(StrictModel):
    """First WebSocket frame, shared with the conductor's ASR bridge."""

    type: Literal["input.start"] = "input.start"
    protocol_version: str = PROTOCOL_VERSION
    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)
    sample_rate_hz: int = Field(default=16_000, ge=8_000, le=192_000)
    channels: int = Field(default=1, ge=1, le=8)
    encoding: Literal["pcm_s16le"] = "pcm_s16le"
    language: str | None = Field(default=None, max_length=24)
    needs_vision: bool = False
    target_node: str | None = Field(default=None, max_length=240)
    required_capabilities: set[str] = Field(default_factory=set)
    requested_permissions: set[str] = Field(default_factory=set)
    minicpm_session_id: str | None = Field(default=None, max_length=160)

    @field_validator("protocol_version")
    @classmethod
    def validate_protocol_version(cls, value: str) -> str:
        if value != PROTOCOL_VERSION:
            raise ValueError(f"unsupported protocol version: {value}")
        return value


class ASRControl(StrictModel):
    type: Literal["input.commit", "input.cancel"]


class TranscriptEvent(StrictModel):
    type: Literal["input.transcript.partial", "input.transcript.final", "error"]
    text: str = ""
    confidence: float | None = Field(default=None, ge=0, le=1)
    language: str | None = Field(default=None, max_length=24)
    detail: str | None = Field(default=None, max_length=500)


def partial_event(
    text: str, *, language: str | None = None, confidence: float | None = None
) -> TranscriptEvent:
    return TranscriptEvent(
        type="input.transcript.partial",
        text=text,
        language=language,
        confidence=confidence,
    )


def final_event(
    text: str, *, language: str | None = None, confidence: float | None = None
) -> TranscriptEvent:
    return TranscriptEvent(
        type="input.transcript.final",
        text=text,
        language=language,
        confidence=confidence,
    )


def error_event(detail: str) -> TranscriptEvent:
    return TranscriptEvent(type="error", detail=detail[:500])
