"""TDD tests for compiler/stages/acquire.py — all offline via recorded fixtures."""

from __future__ import annotations

from pathlib import Path

import httpx
from compiler.stages.acquire import acquire_all, extract_text

FIXTURES_HTTP = Path(__file__).parent.parent / "fixtures" / "http"


def _fixture_transport() -> httpx.MockTransport:
    """HTTPX mock transport that returns pre-recorded fixture responses."""

    def handler(request: httpx.Request) -> httpx.Response:
        url = str(request.url)
        # Gutenberg
        if "gutenberg.org" in url and "2680" in url:
            body = (FIXTURES_HTTP / "gutenberg_2680.txt").read_bytes()
            return httpx.Response(200, content=body)
        if "gutenberg.org" in url and "55317" in url:
            body = (FIXTURES_HTTP / "gutenberg_2680.txt").read_bytes()  # reuse fixture
            return httpx.Response(200, content=body)
        # Wikipedia
        if "wikipedia.org/w/api.php" in url and "Marcus_Aurelius" in url:
            body = (FIXTURES_HTTP / "wikipedia_marcus_aurelius.json").read_bytes()
            return httpx.Response(200, content=body)
        # Wikiquote
        if "wikiquote.org" in url and "Marcus_Aurelius" in url:
            body = (FIXTURES_HTTP / "wikiquote_marcus_aurelius.html").read_bytes()
            return httpx.Response(200, content=body)
        return httpx.Response(404, text="Not found in fixture transport")

    return httpx.MockTransport(handler)


def _mock_client() -> httpx.Client:
    return httpx.Client(transport=_fixture_transport())


# ── acquire_all ──────────────────────────────────────────────────────────────


def test_acquire_gutenberg(tmp_path):
    sources = [
        {
            "id": "meditations-gutenberg",
            "fetch_type": "gutenberg",
            "gutenberg_id": 2680,
        }
    ]
    result = acquire_all(sources, tmp_path / "raw", http_client=_mock_client())
    assert "meditations-gutenberg" in result
    path = result["meditations-gutenberg"]
    assert path.exists()
    assert path.suffix == ".txt"
    assert b"Marcus Aurelius" in path.read_bytes()


def test_acquire_wikipedia(tmp_path):
    sources = [
        {
            "id": "wikipedia-marcus-aurelius",
            "fetch_type": "wikipedia",
            "wikipedia_title": "Marcus Aurelius",
        }
    ]
    result = acquire_all(sources, tmp_path / "raw", http_client=_mock_client())
    path = result["wikipedia-marcus-aurelius"]
    assert path.exists()
    assert path.suffix == ".json"


def test_default_client_identifies_mentorcore_to_wikipedia(tmp_path, monkeypatch):
    """Wikipedia can reject the anonymous default python-httpx user agent."""
    seen_headers: dict[str, str] = {}

    def handler(request: httpx.Request) -> httpx.Response:
        seen_headers.update(request.headers)
        body = (FIXTURES_HTTP / "wikipedia_marcus_aurelius.json").read_bytes()
        return httpx.Response(200, content=body)

    real_client = httpx.Client

    def mock_client(*args, **kwargs):
        return real_client(transport=httpx.MockTransport(handler), *args, **kwargs)

    monkeypatch.setattr("compiler.stages.acquire.httpx.Client", mock_client)

    acquire_all(
        [
            {
                "id": "wikipedia-marcus-aurelius",
                "fetch_type": "wikipedia",
                "wikipedia_title": "Marcus Aurelius",
            }
        ],
        tmp_path / "raw",
    )

    assert seen_headers["user-agent"].startswith("MentorCore/")


def test_acquire_url(tmp_path):
    sources = [
        {
            "id": "wikiquote-marcus-aurelius",
            "fetch_type": "url",
            "url": "https://en.wikiquote.org/wiki/Marcus_Aurelius",
        }
    ]
    result = acquire_all(sources, tmp_path / "raw", http_client=_mock_client())
    path = result["wikiquote-marcus-aurelius"]
    assert path.exists()
    assert path.suffix == ".html"


def test_acquire_skips_cached(tmp_path):
    """Second call with same raw_dir should not hit the network."""
    raw_dir = tmp_path / "raw"
    sources = [
        {
            "id": "meditations-gutenberg",
            "fetch_type": "gutenberg",
            "gutenberg_id": 2680,
        }
    ]
    # First call — uses mock client
    acquire_all(sources, raw_dir, http_client=_mock_client())
    # Second call — pass a client that always fails; should succeed from cache
    failing = httpx.Client(transport=httpx.MockTransport(lambda r: httpx.Response(500)))
    result = acquire_all(sources, raw_dir, http_client=failing)
    assert result["meditations-gutenberg"].exists()


def test_acquire_force_refetches(tmp_path):
    """force=True must re-download even when file exists."""
    raw_dir = tmp_path / "raw"
    sources = [
        {
            "id": "meditations-gutenberg",
            "fetch_type": "gutenberg",
            "gutenberg_id": 2680,
        }
    ]
    acquire_all(sources, raw_dir, http_client=_mock_client())
    call_count = [0]

    def counting_handler(request: httpx.Request) -> httpx.Response:
        call_count[0] += 1
        body = (FIXTURES_HTTP / "gutenberg_2680.txt").read_bytes()
        return httpx.Response(200, content=body)

    counting_client = httpx.Client(transport=httpx.MockTransport(counting_handler))
    acquire_all(sources, raw_dir, http_client=counting_client, force=True)
    assert call_count[0] == 1


# ── extract_text ─────────────────────────────────────────────────────────────


def test_extract_text_gutenberg(tmp_path):
    src = FIXTURES_HTTP / "gutenberg_2680.txt"
    text = extract_text(src, "gutenberg")
    assert "Marcus Aurelius" in text
    assert len(text) > 100


def test_extract_text_wikipedia(tmp_path):
    src = FIXTURES_HTTP / "wikipedia_marcus_aurelius.json"
    text = extract_text(src, "wikipedia")
    assert "Stoic" in text
    assert len(text) > 50


def test_extract_text_url_html(tmp_path):
    src = FIXTURES_HTTP / "wikiquote_marcus_aurelius.html"
    text = extract_text(src, "url")
    assert text is not None
    assert len(text) > 0
