"""A2A request/response envelope models.

Pydantic v2 models for the A2A wire format.  In local mode these are
used purely as validated data containers — no serialization to JSON
actually occurs over a network.

Wire format follows JSON-RPC 2.0 specification:
  Request:  {"jsonrpc": "2.0", "id": ..., "method": ..., "params": {...}}
  Response: {"jsonrpc": "2.0", "id": ..., "result": {...}}  (success)
            {"jsonrpc": "2.0", "id": ..., "error": {...}}   (failure)
"""

from __future__ import annotations

from datetime import UTC, datetime
from typing import Any

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

# ---------------------------------------------------------------------------
# Version constants
# ---------------------------------------------------------------------------

JSONRPC_VERSION: str = "2.0"


class A2aVersion:
    """A2A protocol version constants.

    Kept for backward compatibility — the wire format now uses the
    JSON-RPC 2.0 ``jsonrpc`` field with value ``"2.0"`` rather than
    a proprietary ``a2a_version`` field.
    """

    CURRENT: str = JSONRPC_VERSION
    SUPPORTED: tuple[str, ...] = (JSONRPC_VERSION,)


# ---------------------------------------------------------------------------
# Helper factories
# ---------------------------------------------------------------------------


def _ulid_factory() -> str:
    """Generate a new ULID string."""
    return str(ULID())


def _iso_now_factory() -> str:
    """Return the current UTC time as an ISO-8601 string."""
    return datetime.now(tz=UTC).isoformat()


# ---------------------------------------------------------------------------
# Models
# ---------------------------------------------------------------------------


class A2aErrorDetail(BaseModel):
    """Structured error payload inside an :class:`A2aResponse`.

    Matches the JSON-RPC 2.0 error object structure:
    ``{"code": ..., "message": ..., "data": {...}}``

    Per JSON-RPC 2.0 specification (Section 5.1), ``code`` must be an integer.
    Standard JSON-RPC 2.0 error codes are negative integers in the range
    -32768 to -32000.  Application-defined codes must be outside this range.
    """

    model_config = ConfigDict(strict=False)

    code: int
    message: str
    data: dict[str, Any] = {}

    @field_validator("message")
    @classmethod
    def _must_be_non_empty(cls, value: str) -> str:
        if not value:
            raise ValueError("field must not be empty")
        return value


class A2aRequest(BaseModel):
    """Inbound A2A operation envelope — JSON-RPC 2.0 compliant.

    Wire format::

        {
            "jsonrpc": "2.0",
            "id": "01HXRCF1...",
            "method": "_cleveragents/plan/status",
            "params": {"plan_id": "..."}
        }
    """

    model_config = ConfigDict(strict=False)

    jsonrpc: str = Field(default=JSONRPC_VERSION)
    id: str = Field(default="")
    method: str
    params: dict[str, Any] = {}

    @field_validator("jsonrpc")
    @classmethod
    def _jsonrpc_must_be_2_0(cls, value: str) -> str:
        if value != JSONRPC_VERSION:
            raise ValueError(f"jsonrpc must be '2.0', got {value!r}")
        return value

    @field_validator("method")
    @classmethod
    def _method_non_empty(cls, value: str) -> str:
        if not value or not value.strip():
            raise ValueError("method must not be empty")
        return value

    @model_validator(mode="after")
    def _default_id(self) -> A2aRequest:
        if not self.id:
            self.id = _ulid_factory()
        return self


class A2aResponse(BaseModel):
    """Outbound A2A result envelope — JSON-RPC 2.0 compliant.

    Success wire format::

        {"jsonrpc": "2.0", "id": "...", "result": {...}}

    Error wire format::

        {"jsonrpc": "2.0", "id": "...", "error": {"code": ..., "message": ...,
        "data": {...}}}
    """

    model_config = ConfigDict(strict=False)

    jsonrpc: str = Field(default=JSONRPC_VERSION)
    id: str
    result: dict[str, Any] | None = None
    error: A2aErrorDetail | None = None

    @field_validator("jsonrpc")
    @classmethod
    def _jsonrpc_must_be_2_0(cls, value: str) -> str:
        if value != JSONRPC_VERSION:
            raise ValueError(f"jsonrpc must be '2.0', got {value!r}")
        return value

    @model_validator(mode="after")
    def _result_xor_error(self) -> A2aResponse:
        """Exactly one of result or error must be set."""
        if self.result is None and self.error is None:
            raise ValueError("A2aResponse must have either 'result' or 'error'")
        if self.result is not None and self.error is not None:
            raise ValueError("A2aResponse must not have both 'result' and 'error'")
        return self


class A2aEvent(BaseModel):
    """Server-sent event envelope for plan progress and streaming."""

    model_config = ConfigDict(strict=False)

    event_id: str = ""
    event_type: str
    plan_id: str | None = None
    data: dict[str, Any] = {}
    timestamp: str = ""

    @field_validator("event_type")
    @classmethod
    def _event_type_non_empty(cls, value: str) -> str:
        if not value or not value.strip():
            raise ValueError("event_type must not be empty")
        return value

    @model_validator(mode="after")
    def _set_defaults(self) -> A2aEvent:
        if not self.event_id:
            self.event_id = _ulid_factory()
        if not self.timestamp:
            self.timestamp = _iso_now_factory()
        return self


__all__ = [
    "JSONRPC_VERSION",
    "A2aErrorDetail",
    "A2aEvent",
    "A2aRequest",
    "A2aResponse",
    "A2aVersion",
]
