"""AI result and conversational event contracts."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

from .media import EncodedImage, ImageFrame


@dataclass(frozen=True, slots=True)
class BoundingBox:
    x_min: float
    y_min: float
    x_max: float
    y_max: float


@dataclass(frozen=True, slots=True)
class Detection:
    label: str
    confidence: float
    box: BoundingBox
    track_id: str | None = None


@dataclass(frozen=True, slots=True)
class DetectionResult:
    detections: tuple[Detection, ...]
    model_id: str
    inference_ms: float
    # Human-readable registration metadata.  It is optional for compatibility
    # with existing third-party producers of DetectionResult/v1.
    model_name: str = ""
    # Internal, zero-copy frame reference for downstream evidence generation.
    # It must never be serialized by a generic platform connector.
    source_frame: ImageFrame | None = field(
        default=None,
        repr=False,
        compare=False,
        metadata={"transient": True},
    )


@dataclass(frozen=True, slots=True)
class DetectionAlert:
    """A temporal detection rule that is ready for a downstream interface.

    A DetectionResult describes one inferred frame.  DetectionAlert is emitted
    only after a stateful rule has observed enough distinct frames inside its
    configured time window.  Keeping the two contracts separate lets raw
    detections be recorded or inspected without accidentally calling an alert
    endpoint for every frame.
    """

    event_id: str
    rule_id: str
    model_id: str
    model_name: str
    labels: tuple[str, ...]
    scope: str
    scope_id: str
    hit_count: int
    window_ms: float
    first_seen_ns: int
    last_seen_ns: int
    triggered_at_ns: int
    max_confidence: float
    detections: tuple[Detection, ...]
    image: EncodedImage | None = None


@dataclass(frozen=True, slots=True)
class TextEvent:
    text: str
    role: str = "assistant"
    final: bool = True


@dataclass(frozen=True, slots=True)
class ChatTurn:
    session_id: str
    user_text: str
    history: tuple[dict[str, Any], ...] = ()
