"""Tests for StandaloneEventLogger and generate_local_run_id."""

import io

import pytest

from .standalone import StandaloneEvent, StandaloneEventLogger, generate_local_run_id, _compact_json


class TestStandaloneEventLogger:
    def _make_logger(self) -> tuple[StandaloneEventLogger, io.StringIO]:
        buf = io.StringIO()
        logger = StandaloneEventLogger("test-run-001", output=buf)
        return logger, buf

    def test_emit_increments_seq(self) -> None:
        logger, _ = self._make_logger()
        e1 = logger.emit("run.started")
        e2 = logger.emit("model.start", model="gpt-4o-mini")
        e3 = logger.emit("model.delta", text="hello")
        assert e1.seq == 1
        assert e2.seq == 2
        assert e3.seq == 3

    def test_format_run_started(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("run.started")
        output = buf.getvalue()
        assert "[run.started]" in output
        assert "run_id=test-run-001" in output
        assert "(standalone mode)" in output

    def test_format_model_delta(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("model.delta", text="Hello world")
        output = buf.getvalue()
        assert "[model.delta]" in output
        assert '"Hello world"' in output

    def test_format_model_delta_truncation(self) -> None:
        logger, buf = self._make_logger()
        long_text = "x" * 100
        logger.emit("model.delta", text=long_text)
        output = buf.getvalue()
        # Should be truncated at 60 chars + "..."
        assert "..." in output
        assert "x" * 61 not in output

    def test_format_tool_call_started(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("tool.call.started", tool_name="greet", arguments={"name": "Alice"}, call_id="c1")
        output = buf.getvalue()
        assert "[tool.call.started]" in output
        assert "tool=greet" in output
        assert "Alice" in output

    def test_format_tool_call_completed(self) -> None:
        logger, buf = self._make_logger()
        logger.emit(
            "tool.call.completed",
            tool_name="greet",
            result="Hello, Alice!",
            call_id="c1",
            duration_ms=1.5,
        )
        output = buf.getvalue()
        assert "[tool.call.completed]" in output
        assert "tool=greet" in output
        assert 'result="Hello, Alice!"' in output
        assert "duration=1.5ms" in output

    def test_format_run_completed(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("run.completed", duration_s=1.2, total_tokens=320, cost_usd=0.0020)
        output = buf.getvalue()
        assert "[run.completed]" in output
        assert "duration=1.2s" in output
        assert "tokens=320" in output
        assert "cost=$0.0020" in output

    def test_format_run_failed(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("run.failed", error="budget_exceeded: token limit 100 exceeded (130)")
        output = buf.getvalue()
        assert "[run.failed]" in output
        assert "budget_exceeded" in output

    def test_format_message_emitted(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("message.emitted", content="Hello, Alice!", role="assistant")
        output = buf.getvalue()
        assert "[message.emitted]" in output
        assert '"Hello, Alice!"' in output

    def test_format_agent_updated(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("agent.updated", agent="researcher")
        output = buf.getvalue()
        assert "[agent.updated]" in output
        assert "agent=researcher" in output

    def test_events_list_accumulates(self) -> None:
        logger, _ = self._make_logger()
        assert logger.events == []
        logger.emit("run.started")
        logger.emit("model.delta", text="hi")
        logger.emit("run.completed", duration_s=0.5, total_tokens=100, cost_usd=0.001)
        events = logger.events
        assert len(events) == 3
        assert events[0].type == "run.started"
        assert events[1].type == "model.delta"
        assert events[2].type == "run.completed"
        # events property returns a copy
        logger.emit("run.failed", error="test")
        assert len(events) == 3  # original list unchanged

    def test_event_has_timestamp(self) -> None:
        logger, _ = self._make_logger()
        e = logger.emit("run.started")
        assert e.ts >= 0.0

    def test_unknown_event_type(self) -> None:
        logger, buf = self._make_logger()
        logger.emit("custom.event", data="test123")
        output = buf.getvalue()
        assert "[custom.event]" in output
        assert "test123" in output


class TestGenerateLocalRunId:
    def test_starts_with_local_prefix(self) -> None:
        run_id = generate_local_run_id()
        assert run_id.startswith("local_")

    def test_unique_ids(self) -> None:
        ids = {generate_local_run_id() for _ in range(100)}
        assert len(ids) == 100

    def test_length_is_consistent(self) -> None:
        run_id = generate_local_run_id()
        # "local_" (6) + 12 hex (ts) + 10 hex (rand) = 28
        assert len(run_id) == 28


class TestCompactJson:
    def test_dict_to_compact(self) -> None:
        result = _compact_json({"name": "Alice", "age": 30})
        assert result == '{"name":"Alice","age":30}'

    def test_string_json_parsed(self) -> None:
        result = _compact_json('{"name": "Alice"}')
        assert result == '{"name":"Alice"}'

    def test_plain_string_returned_as_is(self) -> None:
        result = _compact_json("not json")
        assert result == "not json"

    def test_unicode_preserved(self) -> None:
        result = _compact_json({"text": "Привет"})
        assert "Привет" in result
