"""Tests for JsonSink (spens/sinks/jsonl.py)."""

from __future__ import annotations

import io
import json

from spens.events import SCHEMA_VERSION, Emitter, Event
from spens.sinks import get_sink
from spens.sinks.jsonl import JsonSink
from spens.sinks.tty import TtySink


class CountingStream:
    """A stream wrapper that counts flush() calls and records writes."""

    def __init__(self) -> None:
        self.buffer = io.StringIO()
        self.flushes = 0

    def write(self, text: str) -> int:
        return self.buffer.write(text)

    def flush(self) -> None:
        self.flushes += 1


def _event(name: str, **data) -> Event:
    return Event(
        event=name, session_id="sid", timestamp="2026-01-01T00:00:00+00:00", data=data
    )


def test_one_json_object_per_line() -> None:
    stream = io.StringIO()
    sink = JsonSink(stream)
    for event in [
        _event("started", session_dir="/a"),
        _event("warning", message="[spens] Warning: x"),
        _event("agent_output", chunk="agent says hi"),
        _event("finished", exit_code=0),
    ]:
        sink.on_event(event)
    lines = stream.getvalue().splitlines()
    assert len(lines) == 4
    parsed = [json.loads(line) for line in lines]
    assert [p["event"] for p in parsed] == [
        "started", "warning", "agent_output", "finished"
    ]
    for payload in parsed:
        assert payload["schema_version"] == SCHEMA_VERSION
        assert payload["session_id"] == "sid"
        assert payload["timestamp"] == "2026-01-01T00:00:00+00:00"
    assert parsed[2]["data"] == {"chunk": "agent says hi"}


def test_flush_called_per_event() -> None:
    stream = CountingStream()
    sink = JsonSink(stream)
    sink.on_event(_event("started"))
    sink.on_event(_event("build_started", image="i"))
    sink.on_event(_event("warning", message="w"))
    assert stream.flushes == 3


def test_agent_output_streams_to_stdout_like_every_event() -> None:
    stream = io.StringIO()
    sink = JsonSink(stream)
    sink.on_event(_event("agent_output", chunk="partial agent line"))
    payload = json.loads(stream.getvalue())
    assert payload["event"] == "agent_output"
    assert payload["data"]["chunk"] == "partial agent line"


def test_get_sink_factory() -> None:
    import pytest

    assert isinstance(get_sink("tty"), TtySink)
    assert isinstance(get_sink("jsonl"), JsonSink)
    assert get_sink("background") is None
    with pytest.raises(ValueError, match="unknown output mode"):
        get_sink("nope")


def test_emitter_driven_rendering() -> None:
    stream = io.StringIO()
    emitter = Emitter("sid", [JsonSink(stream)])
    emitter.emit("started", session_dir="/a")
    emitter.close()
    payload = json.loads(stream.getvalue())
    assert payload["event"] == "started"
