from __future__ import annotations

import uuid
from datetime import datetime, timezone
from typing import Annotated, Literal

from pydantic import BaseModel, Field, TypeAdapter

from ..errors import ErrorCode, OpName

EventPhase = Literal["start", "finish"]


def _utcnow() -> datetime:
    return datetime.now(tz=timezone.utc)


class EventPayloadPolicy(BaseModel):
    """Controls how much potentially sensitive/large data is included in events."""

    # Exec output can be noisy and sensitive; default off.
    include_exec_output: bool = Field(default=False)

    # When enabled, bound output sizes.
    max_stdout_chars: int = Field(default=8_000, ge=0)
    max_stderr_chars: int = Field(default=8_000, ge=0)

    # For write events, we only include a best-effort byte count (never file bytes).
    include_write_len: bool = Field(default=True)


class SandboxSessionEventBase(BaseModel):
    """Shared fields for all sandbox audit events."""

    version: int = Field(default=1)

    event_id: uuid.UUID = Field(default_factory=uuid.uuid4)
    ts: datetime = Field(default_factory=_utcnow)

    session_id: uuid.UUID
    seq: int

    op: OpName
    phase: EventPhase

    # Correlates start/finish records for an operation.
    # When SDK tracing is active, this is the SDK span id for the operation.
    span_id: str
    parent_span_id: str | None = None
    trace_id: str | None = None

    # Operation-specific metadata (paths, argv, timings, etc.)
    data: dict[str, object] = Field(default_factory=dict)


class SandboxSessionStartEvent(SandboxSessionEventBase):
    """The start event for an operation."""

    phase: Literal["start"] = Field(default="start")


class SandboxSessionFinishEvent(SandboxSessionEventBase):
    """The finish event for an operation."""

    phase: Literal["finish"] = Field(default="finish")

    ok: bool
    duration_ms: float

    error_code: ErrorCode | None = None
    error_type: str | None = None
    error_message: str | None = None
    error_retryable: bool | None = None

    # Optional exec outputs (truncated / opt-in via policy).
    stdout: str | None = None
    stderr: str | None = None

    # Raw exec outputs (bytes) for per-sink/per-op policy application.
    # These are excluded from serialization (JSONL / HTTP) by default.
    stdout_bytes: bytes | None = Field(default=None, exclude=True)
    stderr_bytes: bytes | None = Field(default=None, exclude=True)


# Discriminated union keyed by `phase`.
SandboxSessionEvent = Annotated[
    SandboxSessionStartEvent | SandboxSessionFinishEvent,
    Field(discriminator="phase"),
]
_SANDBOX_SESSION_EVENT_ADAPTER: TypeAdapter[SandboxSessionEvent] = TypeAdapter(SandboxSessionEvent)


def validate_sandbox_session_event(obj: object) -> SandboxSessionEvent:
    """Parse an event payload (e.g. from JSON) into the correct phase-specific model."""

    return _SANDBOX_SESSION_EVENT_ADAPTER.validate_python(obj)
