"""Unit tests for SkilarkClient.

All HTTP calls are intercepted with httpx.MockTransport so no real network
traffic is produced.  Each test wires up exactly the response it expects,
keeping tests self-contained and deterministic.
"""

import json

import httpx
import pytest

from skilark_cli.client import SkilarkClient


# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------

def _make_client(handler, user_id: str | None = None) -> SkilarkClient:
    """Return a SkilarkClient backed by a MockTransport."""
    return SkilarkClient(
        base_url="https://test",
        user_id=user_id,
        transport=httpx.MockTransport(handler),
    )


# ---------------------------------------------------------------------------
# create_user
# ---------------------------------------------------------------------------

class TestCreateUser:
    def test_posts_to_correct_path(self):
        def handler(request):
            assert request.url.path == "/v1/users"
            return httpx.Response(201, json={"id": "uuid-1", "topics": ["python"]})

        client = _make_client(handler)
        client.create_user(topics=["python"])

    def test_sends_topics_in_body(self):
        def handler(request):
            body = json.loads(request.content)
            assert body["topics"] == ["python"]
            return httpx.Response(201, json={"id": "uuid-1", "topics": ["python"]})

        client = _make_client(handler)
        client.create_user(topics=["python"])

    def test_returns_parsed_json(self):
        def handler(request):
            return httpx.Response(201, json={"id": "uuid-1", "topics": ["python"]})

        client = _make_client(handler)
        user = client.create_user(topics=["python"])
        assert user["id"] == "uuid-1"

    def test_raises_on_server_error(self):
        def handler(request):
            return httpx.Response(500, json={"error": "internal"})

        client = _make_client(handler)
        with pytest.raises(httpx.HTTPStatusError):
            client.create_user(topics=["python"])


# ---------------------------------------------------------------------------
# get_next_challenge
# ---------------------------------------------------------------------------

class TestGetNextChallenge:
    def test_returns_challenge_on_200(self):
        def handler(request):
            return httpx.Response(200, json={"id": "ch1", "question": "What prints?"})

        client = _make_client(handler, user_id="uuid-1")
        ch = client.get_next_challenge(topics=["python"])
        assert ch["id"] == "ch1"

    def test_returns_none_on_404(self):
        def handler(request):
            return httpx.Response(404, json={"error": "no challenges"})

        client = _make_client(handler, user_id="uuid-1")
        result = client.get_next_challenge(topics=["python"])
        assert result is None

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["X-Skilark-User"] == "uuid-1"
            return httpx.Response(200, json={"id": "ch1", "question": "Q"})

        client = _make_client(handler, user_id="uuid-1")
        client.get_next_challenge(topics=["python"])

    def test_includes_topics_in_query_string(self):
        def handler(request):
            assert "python" in str(request.url)
            return httpx.Response(200, json={"id": "ch1", "question": "Q"})

        client = _make_client(handler, user_id="uuid-1")
        client.get_next_challenge(topics=["python"])

    def test_no_user_header_when_user_id_absent(self):
        def handler(request):
            assert "X-Skilark-User" not in request.headers
            return httpx.Response(200, json={"id": "ch1", "question": "Q"})

        client = _make_client(handler)
        client.get_next_challenge(topics=["python"])

    def test_raises_on_non_404_error(self):
        def handler(request):
            return httpx.Response(503, json={"error": "unavailable"})

        client = _make_client(handler, user_id="uuid-1")
        with pytest.raises(httpx.HTTPStatusError):
            client.get_next_challenge(topics=["python"])


# ---------------------------------------------------------------------------
# complete_challenge
# ---------------------------------------------------------------------------

class TestCompleteChallenge:
    def test_posts_to_correct_path(self):
        def handler(request):
            assert request.url.path == "/v1/challenges/ch1/complete"
            return httpx.Response(200, json={"status": "ok"})

        client = _make_client(handler, user_id="uuid-1")
        client.complete_challenge("ch1", answer="1 2", correct=True, self_corrected=False)

    def test_sends_correct_body(self):
        def handler(request):
            body = json.loads(request.content)
            assert body["correct"] is True
            assert body["self_corrected"] is False
            assert body["answer"] == "1 2"
            return httpx.Response(200, json={"status": "ok"})

        client = _make_client(handler, user_id="uuid-1")
        client.complete_challenge("ch1", answer="1 2", correct=True, self_corrected=False)

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["X-Skilark-User"] == "uuid-1"
            return httpx.Response(200, json={"status": "ok"})

        client = _make_client(handler, user_id="uuid-1")
        client.complete_challenge("ch1", answer="x", correct=False, self_corrected=True)

    def test_raises_on_error_response(self):
        def handler(request):
            return httpx.Response(400, json={"error": "bad request"})

        client = _make_client(handler, user_id="uuid-1")
        with pytest.raises(httpx.HTTPStatusError):
            client.complete_challenge("ch1", answer="x", correct=True, self_corrected=False)


# ---------------------------------------------------------------------------
# get_stats
# ---------------------------------------------------------------------------

class TestGetStats:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.url.path == "/v1/users/me/stats"
            return httpx.Response(200, json={"streak": 7, "total_completed": 47})

        client = _make_client(handler, user_id="uuid-1")
        client.get_stats()

    def test_returns_parsed_stats(self):
        def handler(request):
            return httpx.Response(200, json={"streak": 7, "total_completed": 47})

        client = _make_client(handler, user_id="uuid-1")
        stats = client.get_stats()
        assert stats["streak"] == 7
        assert stats["total_completed"] == 47

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["X-Skilark-User"] == "uuid-1"
            return httpx.Response(200, json={"streak": 0, "total_completed": 0})

        client = _make_client(handler, user_id="uuid-1")
        client.get_stats()

    def test_raises_on_error_response(self):
        def handler(request):
            return httpx.Response(401, json={"error": "unauthorized"})

        client = _make_client(handler, user_id="uuid-1")
        with pytest.raises(httpx.HTTPStatusError):
            client.get_stats()
