from __future__ import annotations

import json
from collections.abc import AsyncIterator
from dataclasses import dataclass
from typing import Any


@dataclass(slots=True, frozen=True)
class SSEEvent:
    event: str = "message"
    data: str = ""
    event_id: str | None = None
    retry_ms: int | None = None

    def json(self) -> dict[str, Any]:
        value = json.loads(self.data or "{}")
        if not isinstance(value, dict):
            raise ValueError("SSE data must decode to a JSON object")
        return value


async def parse_sse_lines(lines: AsyncIterator[str]) -> AsyncIterator[SSEEvent]:
    """Parse WHATWG-style SSE lines, including multiline data and comments."""

    event_name = "message"
    event_id: str | None = None
    retry_ms: int | None = None
    data_lines: list[str] = []

    async for raw_line in lines:
        line = raw_line.rstrip("\r\n")
        if not line:
            if data_lines:
                yield SSEEvent(event_name, "\n".join(data_lines), event_id, retry_ms)
            event_name = "message"
            retry_ms = None
            data_lines = []
            continue
        if line.startswith(":"):
            continue
        field, separator, value = line.partition(":")
        if separator and value.startswith(" "):
            value = value[1:]
        if field == "event":
            event_name = value or "message"
        elif field == "data":
            data_lines.append(value)
        elif field == "id" and "\x00" not in value:
            event_id = value
        elif field == "retry":
            try:
                retry_ms = int(value)
            except ValueError:
                pass

    if data_lines:
        yield SSEEvent(event_name, "\n".join(data_lines), event_id, retry_ms)
