"""autoplay_sdk.models — Typed payload models for Autoplay SSE events.

These dataclasses mirror the JSON payloads emitted by the Autoplay event
connector pipeline.  All public fields match the wire format exactly so
``ActionsPayload.from_dict(raw)`` and ``SummaryPayload.from_dict(raw)``
are lossless round-trips.

Each model exposes a ``to_text()`` method that returns an embedding-ready
plain-text representation of the payload — suitable for direct use as the
``input`` to any embedding API (OpenAI, Cohere, Hugging Face, etc.).
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass
class SlimAction:
    """A single UI action extracted from a user session.

    Attributes:
        index:           0-based position of this action in the session sequence.
        type:            Event type in lowercase (e.g. ``"pageview"``, ``"click"``,
                         ``"submit"``).
        title:           Human-readable label for the page or element
                         (e.g. ``"Page Load: Dashboard"`` or ``"Export CSV button"``).
        description:     Natural-language description of what the user did
                         (e.g. ``"User landed on the main Dashboard page"``).
        timestamp_start: Unix timestamp (float) when the action began.
        timestamp_end:   Unix timestamp (float) when the action ended.  For the
                         last action in a batch this equals ``timestamp_start``.
        raw_url:         Original URL as captured, before canonicalization.
        canonical_url:   Normalised URL with dynamic path segments collapsed to
                         ``:id`` (e.g. ``"https://app.example.com/projects/:id"``).
    """

    title: str
    description: str
    canonical_url: str
    index: int = 0
    type: str = ""
    timestamp_start: float = 0.0
    timestamp_end: float = 0.0
    raw_url: str = ""

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "SlimAction":
        """Construct a ``SlimAction`` from a raw pipeline dict."""
        return cls(
            title=data.get("title") or "",
            description=data.get("description") or "",
            canonical_url=data.get("canonical_url") or "",
            index=data.get("index") or 0,
            type=data.get("type") or "",
            timestamp_start=data.get("timestamp_start") or 0.0,
            timestamp_end=data.get("timestamp_end") or 0.0,
            raw_url=data.get("raw_url") or "",
        )

    def to_text(self) -> str:
        """Return a one-line text representation for embedding or logging.

        Format: ``[{index}] {type}: {description} — {canonical_url}``

        Example::

            [0] pageview: User landed on the main Dashboard page — https://app.example.com/dashboard
            [1] click: User clicked Export CSV button — https://app.example.com/dashboard
        """
        return f"[{self.index}] {self.type}: {self.description} — {self.canonical_url}"


@dataclass
class ActionsPayload:
    """A batch of UI actions for a single session, as emitted by the pipeline.

    Delivered via the SSE stream when the connector has extracted and
    forwarded a batch of user interactions.

    Attributes:
        product_id:   Connector product identifier.
        session_id:   User session identifier (may be ``None`` for
                      anonymous sessions before identity linking).
        user_id:      External user identifier passed at Intercom boot
                      (typically the connector's ``distinct_id``).  ``None``
                      when the session is fully anonymous.
        email:        User e-mail if available from the identity store.
        actions:      Ordered list of UI actions in this batch.
        count:        Number of actions in this batch (equals
                      ``len(actions)``).
        forwarded_at: Unix timestamp (float) when the connector forwarded
                      this batch.
    """

    product_id: str
    session_id: str | None
    user_id: str | None
    email: str | None
    actions: list[SlimAction]
    count: int
    forwarded_at: float

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "ActionsPayload":
        """Construct an ``ActionsPayload`` from a raw SSE payload dict."""
        raw_actions = data.get("actions") or []
        return cls(
            product_id=data.get("product_id") or "",
            session_id=data.get("session_id") or None,
            user_id=data.get("user_id") or None,
            email=data.get("email") or None,
            actions=[SlimAction.from_dict(a) for a in raw_actions],
            count=data.get("count") or 0,
            forwarded_at=data.get("forwarded_at") or 0.0,
        )

    def to_text(self) -> str:
        """Return an embedding-ready plain-text representation of this batch.

        Format::

            Session <session_id> — <N> actions
            [0] <type>: <description> — <canonical_url>
            [1] <type>: <description> — <canonical_url>
            ...

        The ``session_id`` line uses ``"unknown"`` when the session has not
        yet been linked to an identity.

        Example::

            Session ps_abc123 — 3 actions
            [0] pageview: User landed on the main Dashboard page — https://app.example.com/dashboard
            [1] click: User clicked Export CSV button — https://app.example.com/dashboard
            [2] click: User opened billing settings — https://app.example.com/settings/billing
        """
        sid = self.session_id or "unknown"
        lines = [f"Session {sid} — {len(self.actions)} actions"]
        for action in self.actions:
            lines.append(action.to_text())
        return "\n".join(lines)


@dataclass
class SummaryPayload:
    """An LLM-generated prose summary of a user session.

    Delivered via the SSE stream when the connector's summariser has
    condensed a session's action history into a compact paragraph.  This
    replaces the raw action list for context-window efficiency in RAG
    pipelines.

    Attributes:
        product_id:   Connector product identifier.
        session_id:   User session identifier.
        summary:      Prose description of what the user did during the
                      session up to this point.
        replaces:     Number of individual actions this summary replaces.
        forwarded_at: Unix timestamp (float) when the connector forwarded
                      this summary.
    """

    product_id: str
    session_id: str | None
    summary: str
    replaces: int
    forwarded_at: float

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "SummaryPayload":
        """Construct a ``SummaryPayload`` from a raw SSE payload dict."""
        return cls(
            product_id=data.get("product_id") or "",
            session_id=data.get("session_id") or None,
            summary=data.get("summary") or "",
            replaces=data.get("replaces") or 0,
            forwarded_at=data.get("forwarded_at") or 0.0,
        )

    def to_text(self) -> str:
        """Return the prose summary, ready for embedding or LLM context.

        This is simply the ``summary`` string itself — no transformation
        needed.  The method exists for API symmetry with ``ActionsPayload``
        so callers can call ``.to_text()`` on any payload type without
        branching.

        Example::

            The user navigated to the Dashboard, exported a CSV report,
            then opened account settings to update their billing plan.
        """
        return self.summary


# Type alias exported for use in callback type hints.
AnyPayload = ActionsPayload | SummaryPayload

__all__ = [
    "SlimAction",
    "ActionsPayload",
    "SummaryPayload",
    "AnyPayload",
]
