"""Tests for /api/v1/llm/echo — SSE streaming Claude endpoint.

Gerçek Anthropic API çağrısı yapılmaz; MessageStreamManager mock'lanır.
Bu testler CI'da API key olmadan çalışabilir.
"""

from __future__ import annotations

from collections.abc import AsyncIterator
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from httpx import ASGITransport, AsyncClient

from app.config import get_settings
from app.main import app


def _parse_sse(body: str) -> list[tuple[str, str]]:
    """SSE payload'ını (event, data) tuple listesine çevirir."""
    events: list[tuple[str, str]] = []
    current_event = ""
    current_data: list[str] = []
    for line in body.split("\n"):
        if line.startswith("event: "):
            current_event = line.removeprefix("event: ").strip()
        elif line.startswith("data: "):
            current_data.append(line.removeprefix("data: "))
        elif line == "" and current_event:
            events.append((current_event, "\n".join(current_data)))
            current_event = ""
            current_data = []
    return events


@pytest.mark.asyncio
async def test_echo_returns_503_when_api_key_missing() -> None:
    """ANTHROPIC_API_KEY yoksa endpoint 503 + anlamlı mesaj döner."""
    get_settings.cache_clear()
    with patch.dict("os.environ", {"ANTHROPIC_API_KEY": ""}, clear=False):
        get_settings.cache_clear()
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:
            response = await client.post(
                "/api/v1/llm/echo",
                json={"message": "Merhaba"},
            )
    assert response.status_code == 503
    assert "ANTHROPIC_API_KEY" in response.json()["detail"]
    get_settings.cache_clear()


@pytest.mark.asyncio
async def test_echo_streams_sse_events_in_order() -> None:
    """Mock client ile SSE event sırası: start → delta(x2) → end."""
    get_settings.cache_clear()

    # Anthropic SDK'nın stream context manager davranışını taklit et
    fake_final_message = MagicMock()
    fake_final_message.stop_reason = "end_turn"
    fake_final_message.usage.input_tokens = 100
    fake_final_message.usage.output_tokens = 20
    fake_final_message.usage.cache_creation_input_tokens = 50
    fake_final_message.usage.cache_read_input_tokens = 0

    async def fake_text_stream() -> AsyncIterator[str]:
        yield "Merhaba"
        yield ", vekil."

    fake_stream = MagicMock()
    fake_stream.text_stream = fake_text_stream()
    fake_stream.get_final_message = AsyncMock(return_value=fake_final_message)

    fake_ctx = MagicMock()
    fake_ctx.__aenter__ = AsyncMock(return_value=fake_stream)
    fake_ctx.__aexit__ = AsyncMock(return_value=None)

    fake_client = MagicMock()
    fake_client.messages.stream = MagicMock(return_value=fake_ctx)

    with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-ant-test-dummy"}, clear=False):
        get_settings.cache_clear()
        with patch("app.api.llm.get_anthropic_client", return_value=fake_client):
            transport = ASGITransport(app=app)
            async with AsyncClient(transport=transport, base_url="http://test") as client:
                response = await client.post(
                    "/api/v1/llm/echo",
                    json={"message": "Merhaba Claude"},
                )

    assert response.status_code == 200
    assert response.headers["content-type"].startswith("text/event-stream")

    events = _parse_sse(response.text)
    event_names = [e[0] for e in events]
    assert event_names == ["start", "delta", "delta", "end"]

    # start event model bilgisini içermeli
    import json

    start_data = json.loads(events[0][1])
    assert "model" in start_data

    # delta event'ler sırayla Merhaba + , vekil.
    assert json.loads(events[1][1])["text"] == "Merhaba"
    assert json.loads(events[2][1])["text"] == ", vekil."

    # end event usage bilgisi
    end_data = json.loads(events[3][1])
    assert end_data["stop_reason"] == "end_turn"
    assert end_data["usage"]["input_tokens"] == 100
    assert end_data["usage"]["output_tokens"] == 20
    assert end_data["usage"]["cache_creation_input_tokens"] == 50

    # SDK'ya cache_control ile system prompt geçtiğini doğrula
    fake_client.messages.stream.assert_called_once()
    call_kwargs = fake_client.messages.stream.call_args.kwargs
    assert call_kwargs["model"] == "claude-haiku-4-5"
    assert isinstance(call_kwargs["system"], list)
    assert call_kwargs["system"][0]["cache_control"] == {"type": "ephemeral"}
    assert "TürkPatent" in call_kwargs["system"][0]["text"]

    get_settings.cache_clear()


@pytest.mark.asyncio
async def test_echo_validates_empty_message() -> None:
    """Empty message → 422 (Pydantic min_length=1)."""
    with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-ant-test-dummy"}, clear=False):
        get_settings.cache_clear()
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:
            response = await client.post("/api/v1/llm/echo", json={"message": ""})
    assert response.status_code == 422
    get_settings.cache_clear()
