"""Tests for considered_articles + considered_sections population in
synthesize_query (A14).

The featured passage's article and section are excluded from the
considered lists — these surfaces are for *alternatives* the caller can
pivot to, not duplicates of what's already in citations[].
"""

from __future__ import annotations

from typing import Any

from openzim_mcp.synthesize import (
    _build_considered_articles,
    _build_considered_sections,
)


def test_build_considered_articles_prefers_bundle_title_over_hit_title():
    """Post-a14 sweep pass-2 self-audit: when ``archive_titles`` carries
    a proper bundle title for the entry, ``_build_considered_articles``
    should use it in preference to the hit's path-shaped title. This
    fixes the IEP-archive case where every search hit's title equals
    the entry path (``iep.utm.edu/kantview/``) — humanizing via
    underscore-replace doesn't help there because the path uses slashes,
    not underscores."""
    top_hits = [
        (
            "iep",
            {
                "path": "iep.utm.edu/kantview/",
                "title": "iep.utm.edu/kantview/",
                "score": 0.8,
            },
        ),
    ]
    capped_passages = [
        {
            "cite_id": "iep/iep.utm.edu/kantmind/",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]
    archive_titles = {
        ("iep", "iep.utm.edu/kantview/"): "Immanuel Kant",
    }
    out = _build_considered_articles(
        top_hits, capped_passages, max_n=10, archive_titles=archive_titles
    )
    assert out
    assert (
        out[0]["title"] == "Immanuel Kant"
    ), f"Expected bundle title 'Immanuel Kant', got {out[0]['title']!r}"


def test_build_considered_articles_emits_human_readable_title_when_hit_title_is_path_like():
    """Post-a14 sweep (F5 / A3): when a search hit's ``title`` field
    is missing or equals the underscored path (some search-result
    shapes do this), the considered_articles entry must surface a
    human-readable title so the multi-round response is consistent
    with the regular ``citations[]`` view (which sources titles from
    the bundle and always has spaces, not underscores)."""
    top_hits = [
        ("wiki", {"path": "Big_Rapids,_Michigan", "score": 1.0}),
        ("wiki", {"path": "West_Michigan", "title": "West_Michigan", "score": 0.8}),
    ]
    capped_passages = [
        {
            "cite_id": "wiki/some_other_article",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]
    out = _build_considered_articles(top_hits, capped_passages, max_n=10)
    titles = [a["title"] for a in out]
    # Missing title → human-readable derived from path.
    # Underscored title → underscores replaced with spaces.
    assert titles == ["Big Rapids, Michigan", "West Michigan"]


def test_build_considered_articles_excludes_featured_and_caps_at_3():
    """Top_hits with 6 entries; passage capped to 1 (featured). The
    considered_articles list has the remaining 5 in order, capped at 3."""
    top_hits = [
        (
            "wiki",
            {
                "path": "Big_Rapids,_Michigan",
                "title": "Big Rapids, Michigan",
                "score": 1.0,
            },
        ),
        (
            "wiki",
            {
                "path": "Big_Rapids_Township,_Michigan",
                "title": "Big Rapids Twp",
                "score": 0.7,
            },
        ),
        (
            "wiki",
            {"path": "Ferris_State_University", "title": "Ferris State", "score": 0.6},
        ),
        (
            "wiki",
            {
                "path": "Mecosta_County,_Michigan",
                "title": "Mecosta County",
                "score": 0.5,
            },
        ),
        (
            "wiki",
            {
                "path": "Pere_Marquette_River",
                "title": "Pere Marquette River",
                "score": 0.4,
            },
        ),
        ("wiki", {"path": "Muskegon_River", "title": "Muskegon River", "score": 0.3}),
    ]
    capped_passages = [
        {
            "cite_id": "wiki/Big_Rapids,_Michigan#Notable_people",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.5,
        }
    ]
    result = _build_considered_articles(top_hits, capped_passages, max_n=3)
    assert len(result) == 3
    assert all(a["entry_path"] != "Big_Rapids,_Michigan" for a in result)
    assert result[0]["entry_path"] == "Big_Rapids_Township,_Michigan"
    assert result[1]["entry_path"] == "Ferris_State_University"
    assert result[2]["entry_path"] == "Mecosta_County,_Michigan"


def test_build_considered_articles_empty_when_only_featured():
    """One top_hit, captured as the featured passage → empty list."""
    top_hits = [
        ("wiki", {"path": "Big_Rapids,_Michigan", "title": "Big Rapids", "score": 1.0}),
    ]
    capped_passages = [
        {
            "cite_id": "wiki/Big_Rapids,_Michigan#Notable_people",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.5,
        }
    ]
    result = _build_considered_articles(top_hits, capped_passages, max_n=3)
    assert result == []


def test_build_considered_articles_includes_all_when_no_passages():
    """No featured passage → no exclusion applied; all top_hits pass through (capped at max_n)."""
    top_hits = [
        ("wiki", {"path": "A", "title": "A", "score": 1.0}),
        ("wiki", {"path": "B", "title": "B", "score": 0.5}),
    ]
    result = _build_considered_articles(top_hits, [], max_n=3)
    # With no featured to exclude, all top_hits make it (capped)
    assert len(result) == 2
    assert result[0]["entry_path"] == "A"


def test_build_considered_sections_returns_sections_minus_featured():
    """Featured cites Big_Rapids,_Michigan#Notable_people.
    considered_sections returns all OTHER sections in that article."""
    capped_passages = [
        {
            "cite_id": "wiki/Big_Rapids,_Michigan#Notable_people",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.5,
        }
    ]

    def bundle_lookup(archive_name: str, entry_path: str) -> Any:
        if entry_path == "Big_Rapids,_Michigan":
            return {
                "sections": [
                    {"id": "History", "title": "History"},
                    {"id": "Geography", "title": "Geography"},
                    {"id": "Notable_people", "title": "Notable people"},
                    {"id": "Demographics", "title": "Demographics"},
                ]
            }
        return None

    result = _build_considered_sections(capped_passages, bundle_lookup, max_n=10)
    ids = [s["section_id"] for s in result]
    assert "Notable_people" not in ids
    assert set(ids) == {"History", "Geography", "Demographics"}


def test_build_considered_sections_surfaces_all_sections_when_featured_is_article_level():
    """Post-a14-sweep behavior: when the featured passage is article-
    level (no ``#section_id``), surface ALL the article's sections so
    the next-turn pivot via ``get_section`` is still useful. Previously
    this returned ``[]`` — a strict pessimization for the common live-
    Wikipedia case where the BM25 lead snippet can't be located inside
    rendered_markdown (so section attribution fails for a different
    reason than a missing section list)."""
    capped_passages = [
        {
            "cite_id": "wiki/Big_Rapids,_Michigan",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]

    def bundle_lookup(archive_name: str, entry_path: str) -> Any:
        return {
            "sections": [
                {"id": "History", "title": "History"},
                {"id": "Notable_people", "title": "Notable people"},
            ]
        }

    result = _build_considered_sections(capped_passages, bundle_lookup, max_n=10)
    ids = [s["section_id"] for s in result]
    assert ids == ["History", "Notable_people"]


def test_build_considered_sections_empty_when_no_passages():
    """Zero-hit response → no featured article → []."""
    result = _build_considered_sections([], lambda a, e: None, max_n=10)
    assert result == []


def test_build_considered_sections_caps_at_max_n():
    """20-section article capped at max_n=10."""
    sections = [{"id": f"S{i}", "title": f"Section {i}"} for i in range(20)]
    capped_passages = [
        {
            "cite_id": "wiki/Foo#S0",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]

    def bundle_lookup(archive_name: str, entry_path: str) -> Any:
        return {"sections": sections}

    result = _build_considered_sections(capped_passages, bundle_lookup, max_n=10)
    assert len(result) == 10
    # S0 (featured) is excluded
    assert all(s["section_id"] != "S0" for s in result)


def test_build_considered_sections_handles_bundle_none():
    """Bundle lookup returns None → []."""
    capped_passages = [
        {
            "cite_id": "wiki/Foo#Section",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]

    def none_lookup(archive_name: str, entry_path: str) -> Any:
        return None

    result = _build_considered_sections(capped_passages, none_lookup, max_n=10)
    assert result == []


def test_build_considered_sections_handles_bundle_lookup_raising():
    """Bundle lookup raises → [] (no crash)."""
    capped_passages = [
        {
            "cite_id": "wiki/Foo#Section",
            "text_markdown": "...",
            "rank": 1,
            "score": 1.0,
        }
    ]

    def raising_lookup(archive_name: str, entry_path: str) -> Any:
        raise RuntimeError("bundle build failed")

    result = _build_considered_sections(capped_passages, raising_lookup, max_n=10)
    assert result == []
