import sys
from io import StringIO
from unittest.mock import patch, MagicMock

from agentcost.client import EventClient
from agentcost.event import CostEvent


def _make_event(**kwargs):
    defaults = {
        "agent_id": "test-agent",
        "component_name": "stripe_api",
        "event_type": "tool_call",
        "cost_usd": 0.02,
    }
    defaults.update(kwargs)
    return CostEvent(**defaults)


class TestEventClientDebug:
    def test_debug_mode_prints_to_stderr(self):
        client = EventClient(api_key="", debug=True)
        event = _make_event()

        captured = StringIO()
        with patch("sys.stderr", captured):
            client.enqueue(event)

        output = captured.getvalue()
        assert "[agentcost]" in output
        assert "tool_call" in output
        assert "stripe_api" in output
        assert "$0.020000" in output

    def test_debug_mode_does_not_start_flush_thread(self):
        client = EventClient(api_key="", debug=True)
        assert not hasattr(client, "_flush_thread")

    def test_debug_mode_does_not_queue_events(self):
        client = EventClient(api_key="", debug=True)
        event = _make_event()
        with patch("sys.stderr", StringIO()):
            client.enqueue(event)
        assert len(client._queue) == 0


class TestEventClientFlush:
    @patch("agentcost.client.httpx.post")
    def test_flush_sends_events(self, mock_post):
        mock_post.return_value = MagicMock(
            status_code=200, json=lambda: {"warnings": []}
        )
        client = EventClient(api_key="ac_test", debug=False)
        client._running = False  # prevent background flush

        event = _make_event()
        client._queue.append(event.to_dict())
        client._flush()

        mock_post.assert_called_once()
        call_kwargs = mock_post.call_args
        assert "events" in call_kwargs.kwargs["json"]
        assert len(client._queue) == 0

    @patch("agentcost.client.httpx.post")
    def test_flush_retries_on_server_error(self, mock_post):
        mock_post.side_effect = [
            MagicMock(status_code=500),
            MagicMock(status_code=500),
            MagicMock(status_code=200, json=lambda: {"warnings": []}),
        ]
        client = EventClient(api_key="ac_test", debug=False)
        client._running = False

        client._queue.append(_make_event().to_dict())
        client._flush()

        assert mock_post.call_count == 3

    @patch("agentcost.client.httpx.post")
    def test_flush_does_not_retry_on_auth_error(self, mock_post):
        mock_post.return_value = MagicMock(status_code=401)
        client = EventClient(api_key="ac_bad", debug=False)
        client._running = False

        client._queue.append(_make_event().to_dict())
        client._flush()

        assert mock_post.call_count == 1

    @patch("agentcost.client.httpx.post")
    def test_flush_does_not_retry_on_client_error(self, mock_post):
        mock_post.return_value = MagicMock(status_code=400)
        client = EventClient(api_key="ac_test", debug=False)
        client._running = False

        client._queue.append(_make_event().to_dict())
        client._flush()

        assert mock_post.call_count == 1

    def test_flush_empty_queue_is_noop(self):
        client = EventClient(api_key="ac_test", debug=False)
        client._running = False
        # Should not raise
        client._flush()
