#!/usr/bin/env python3
"""Tests 33-44: Commentary & Reference Works — extended test suite."""

import sys
import os

sys.path.insert(0, os.path.dirname(__file__))

# Import the shared setup module and call setup() to populate global state.
import test_search_cases as tsc

# Pull module-level names after setup (from the prompt's import block).
# We import record directly (a stable function reference), but for mutable
# globals (mods, lex, com_mods, si, bdag, bdb, halot), we rebind after setup.
from test_search_cases import record  # noqa: E402

# Tier data lives in the MCP server module.
from accordance_mcp_server import _COMMENTARY_TIERS, _get_tier_label  # noqa: E402

from accordance_parser.commentary_module import CommentaryModule  # noqa: E402
import re  # noqa: E402

PASS, FAIL = 0, 0

# ── Rebind globals from test_search_cases after setup ─────────────────────
# Because `from test_search_cases import mods` captures the initial None,
# we must re-grab their values after setup() reassigns them.
def _refresh_globals():
    """Re-bind module globals from test_search_cases after setup()."""
    global mods, lex, com_mods, si, bdag, bdb, halot
    mods = tsc.mods
    lex = tsc.lex
    com_mods = tsc.com_mods
    si = tsc.si
    bdag = tsc.bdag
    bdb = tsc.bdb
    halot = tsc.halot


# ── Shared helpers ─────────────────────────────────────────────────────────

def _commentary_path(abbrev: str):
    """Return the filesystem path for a commentary module, or None."""
    if abbrev in com_mods:
        return com_mods[abbrev]["path"]
    # Fuzzy match: case-insensitive
    for k in com_mods:
        if k.lower() == abbrev.lower():
            return com_mods[k]["path"]
    return None


def _load_cm(abbrev: str) -> CommentaryModule | None:
    """Load a CommentaryModule by abbreviation, or return None."""
    path = _commentary_path(abbrev)
    if path is None:
        return None
    try:
        return CommentaryModule(path)
    except Exception:
        return None


def _print_result(test_num: int, name: str, passed: bool, detail: str = ""):
    """Print PASS/FAIL, update counters, and call record()."""
    global PASS, FAIL
    if passed:
        PASS += 1
        print("  [PASS]")
    else:
        FAIL += 1
        if detail:
            print(f"  [FAIL]  Reason: {detail}")
        else:
            print("  [FAIL]")
    # Also log via the shared record() so the original file's _results is kept
    record(test_num, name, passed, detail)


# ═══════════════════════════════════════════════════════════════════════════
# Test Cases 33–44
# ═══════════════════════════════════════════════════════════════════════════


def test_33_three_tier_romans():
    """Test 33: Three-Tier Commentary Comparison — Romans 3:23"""
    name = "Three-Tier Comparison — Romans 3:23"
    print(f"\n{'─' * 60}\nTest 33: {name}")
    try:
        # Pick representatives from each tier
        tier_map = {
            1: ("Hermeneia NT-21", "Scholarly"),
            2: ("JFB",                    "Classic"),
            3: ("ESV Notes",              "Study Notes"),
        }
        tested = 0
        for tier, (abbrev, label) in tier_map.items():
            cm = _load_cm(abbrev)
            if cm is None:
                print(f"  {label} ({abbrev}): not available (skipping)")
                continue
            text = cm.get_section("Romans 3:23")
            cm.close()
            if text is None:
                print(f"  {label} ({abbrev}): get_section returned None")
                continue
            # Check tier classification from _COMMENTARY_TIERS
            expected_tier = _COMMENTARY_TIERS.get(abbrev, 4)
            if expected_tier != tier:
                print(f"  {label} ({abbrev}): tier mismatch — expected {tier}, got {expected_tier}")
            if not isinstance(text, str) or len(text) < 50:
                print(f"  {label} ({abbrev}): text too short ({len(text)} chars) — "
                      f"preview: {text[:80]}")
                continue
            print(f"  Tier {tier} ({label}): {abbrev}  →  {len(text)} chars  ✓")
            tested += 1

        assert tested >= 2, f"Only {tested}/3 tiers tested"
        _print_result(33, name, True)
    except AssertionError as e:
        _print_result(33, name, False, str(e))
    except Exception as e:
        _print_result(33, name, False, f"{type(e).__name__}: {e}")


def test_34_obscure_book_coverage():
    """Test 34: Book Coverage Discovery — Obscure Books (Obadiah, Jude, 2 John)"""
    name = "Book Coverage — Obadiah, Jude, 2 John"
    print(f"\n{'─' * 60}\nTest 34: {name}")
    try:
        target_books = {"Obadiah", "Jude", "2John"}
        coverage = {b: [] for b in target_books}

        for abbrev, info in sorted(com_mods.items()):
            cm = _load_cm(abbrev)
            if cm is None:
                continue
            try:
                books = cm.list_books()
            except Exception:
                cm.close()
                continue
            book_names = [b for b, _ in books]
            for tb in target_books:
                if tb in book_names:
                    coverage[tb].append(abbrev)
            cm.close()

        for tb in target_books:
            labels = coverage[tb]
            if labels:
                print(f"  {tb}: {len(labels)} modules — {labels}")
            else:
                print(f"  {tb}: no coverage")

        obadiah_count = len(coverage.get("Obadiah", []))
        jude_count = len(coverage.get("Jude", []))
        john2_count = len(coverage.get("2John", []))

        assert obadiah_count >= 2, \
            f"Only {obadiah_count} modules cover Obadiah (need ≥ 2)"
        assert jude_count >= 3, \
            f"Only {jude_count} modules cover Jude (need ≥ 3)"
        # 2 John is informational only
        print(f"  2 John coverage: {john2_count} modules")

        _print_result(34, name, True)
    except AssertionError as e:
        _print_result(34, name, False, str(e))
    except Exception as e:
        _print_result(34, name, False, f"{type(e).__name__}: {e}")


def test_35_topic_search_redemption():
    """Test 35: Topic Search 'redemption' Across Commentary Tiers"""
    name = "Topic Search — 'redemption' across commentaries"
    print(f"\n{'─' * 60}\nTest 35: {name}")
    try:
        results = si.search("redemption", source_type="commentary", max_results=20)
        assert len(results) >= 8, \
            f"Only {len(results)} results for 'redemption' (need ≥ 8)"

        sources = set(r[1] for r in results)
        assert len(sources) >= 2, \
            f"Only {len(sources)} distinct source modules (need ≥ 2)"

        # Check for tier variety in results
        tiers_seen = set()
        for r in results:
            tier = _COMMENTARY_TIERS.get(r[1], 4)
            tiers_seen.add(tier)
        assert len(tiers_seen) >= 2, \
            f"Only {len(tiers_seen)} unique tiers (need ≥ 2)"

        print(f"  Found {len(results)} results from {len(sources)} sources")
        print(f"  Tiers represented: {sorted(tiers_seen)} "
              f"({', '.join(_get_tier_label(t) for t in sorted(tiers_seen))})")
        for r in results[:5]:
            tier = _COMMENTARY_TIERS.get(r[1], 4)
            print(f"    [{_get_tier_label(tier)}] {r[1]}: {r[2][:60]}")

        _print_result(35, name, True)
    except AssertionError as e:
        _print_result(35, name, False, str(e))
    except Exception as e:
        _print_result(35, name, False, f"{type(e).__name__}: {e}")


def test_36_fts5_full_text():
    """Test 36: FTS5 Full-Text with include_full_text"""
    name = "FTS5 Full-Text — 'resurrection' full entry retrieval"
    print(f"\n{'─' * 60}\nTest 36: {name}")
    try:
        results = si.search("resurrection", source_type="commentary", max_results=10)
        assert len(results) > 0, "No results for 'resurrection' in commentaries"

        print(f"  Found {len(results)} results")
        full_text_ok = 0
        for r in results:
            source_name = r[1]
            source_id = r[2]
            snippet = r[3]
            full = si.get_full_entry(source_name, source_id)
            if full is None:
                print(f"    {source_name}:{source_id} — get_full_entry returned None")
                continue
            is_longer = len(full) > len(snippet)
            is_big = len(full) > 300
            if is_longer and is_big:
                full_text_ok += 1
                print(f"    {source_name}:{source_id} — "
                      f"full={len(full)} chars > snippet={len(snippet)} chars  ✓")
            else:
                print(f"    {source_name}:{source_id} — "
                      f"full={len(full)}, snippet={len(snippet)} "
                      f"(longer={is_longer}, >300={is_big})")

        assert full_text_ok >= 2, \
            f"Only {full_text_ok} entries passed full-text checks (need ≥ 2)"
        _print_result(36, name, True)
    except AssertionError as e:
        _print_result(36, name, False, str(e))
    except Exception as e:
        _print_result(36, name, False, f"{type(e).__name__}: {e}")


def test_37_browse_chapter_dive_verse():
    """Test 37: Browse Chapter → Dive Into Verse (JFB Genesis 1)"""
    name = "Browse Chapter → Verse — JFB Genesis 1"
    print(f"\n{'─' * 60}\nTest 37: {name}")
    try:
        assert "JFB" in com_mods, "JFB not found in discovered modules"
        cm = CommentaryModule(com_mods["JFB"]["path"])
        try:
            sections = cm.list_chapter_sections("Genesis", 1)
            assert len(sections) >= 3, \
                f"Only {len(sections)} sections in Genesis 1 (need ≥ 3)"
            print(f"  Genesis 1 has {len(sections)} sections:")
            for s in sections[:5]:
                heading = (s.get("heading") or "")[:80]
                print(f"    {s['ref']}: \"{heading}\"")

            full = cm.get_section("Genesis 1:1")
            assert full is not None, "get_section('Genesis 1:1') returned None"
            assert len(full) > 100, \
                f"Genesis 1:1 text too short ({len(full)} chars)"
            print(f"  Genesis 1:1 full text: {len(full)} chars  ✓")
            print(f"    Preview: {full[:120]}")
        finally:
            cm.close()

        _print_result(37, name, True)
    except AssertionError as e:
        _print_result(37, name, False, str(e))
    except Exception as e:
        _print_result(37, name, False, f"{type(e).__name__}: {e}")


def test_38_tier_ranking():
    """Test 38: Quality Tier Ranking Verification"""
    name = "Tier Ranking — Tier 1 Scholarly before Tier 3 Study Notes"
    print(f"\n{'─' * 60}\nTest 38: {name}")
    try:
        results = si.search("Christology", source_type="commentary", max_results=20)
        assert len(results) > 0, "No results for 'Christology'"

        # Determine positions of tier 1 and tier 3 sources
        tier1_positions = []
        tier3_positions = []
        for idx, r in enumerate(results):
            tier = _COMMENTARY_TIERS.get(r[1], 4)
            if tier == 1:
                tier1_positions.append(idx)
            elif tier == 3:
                tier3_positions.append(idx)

        print(f"  Results: {len(results)} total")
        for idx, r in enumerate(results):
            tier = _COMMENTARY_TIERS.get(r[1], 4)
            print(f"    #{idx+1:2d} [{_get_tier_label(tier)} tier={tier}] "
                  f"{r[1]}: {r[2][:50]}")

        # Check: at least one Scholarly source must appear in the top half
        # (allowing for BM25 relevance overrides)
        if tier1_positions:
            print(f"  Tier 1 positions: {[p+1 for p in tier1_positions]}")
        else:
            print("  No Tier 1 sources found in results")
        if tier3_positions:
            print(f"  Tier 3 positions: {[p+1 for p in tier3_positions]}")

        # Not a hard failure if no tier 1/3 in results for this niche query,
        # but if both are present, tier 1 should lead.
        if tier1_positions and tier3_positions:
            first_tier1 = min(tier1_positions)
            first_tier3 = min(tier3_positions)
            assert first_tier1 < first_tier3, \
                f"First Tier 1 at position {first_tier1+1}, first Tier 3 at {first_tier3+1}"
            print(f"  First Tier 1 at #{first_tier1+1}, first Tier 3 at #{first_tier3+1}  ✓")
        elif not tier1_positions and not tier3_positions:
            print("  Neither Tier 1 nor Tier 3 sources in results — no ranking to verify")
        else:
            print("  Only one tier represented — ranking constraint N/A")

        _print_result(38, name, True)
    except AssertionError as e:
        _print_result(38, name, False, str(e))
    except Exception as e:
        _print_result(38, name, False, f"{type(e).__name__}: {e}")


def test_39_jude_commentary():
    """Test 39: Single-Chapter Book — Jude Commentary"""
    name = "Single-Chapter Book — Jude 1:3 from ≥2 modules"
    print(f"\n{'─' * 60}\nTest 39: {name}")
    try:
        # Discover which modules cover Jude
        jude_modules = []
        for abbrev, info in sorted(com_mods.items()):
            cm = _load_cm(abbrev)
            if cm is None:
                continue
            try:
                books = cm.list_books()
                book_names = [b for b, _ in books]
            except Exception:
                cm.close()
                continue
            if "Jude" in book_names:
                jude_modules.append(abbrev)
                cm.close()
            else:
                cm.close()

        print(f"  Modules covering Jude: {jude_modules}")

        # Try to get Jude 1:3 from each
        jude_texts = []
        for abbrev in jude_modules[:6]:  # cap at 6 to avoid excessive loading
            cm = _load_cm(abbrev)
            if cm is None:
                continue
            try:
                text = cm.get_section("Jude 1:3")
                if text and isinstance(text, str) and not text.startswith("[No"):
                    jude_texts.append((abbrev, text))
                    print(f"  {abbrev} Jude 1:3: {len(text)} chars  ✓")
                else:
                    print(f"  {abbrev} Jude 1:3: no text returned")
            except Exception:
                pass
            finally:
                cm.close()

        assert len(jude_texts) >= 2, \
            f"Only {len(jude_texts)} modules returned Jude 1:3 text (need ≥ 2)"
        _print_result(39, name, True)
    except AssertionError as e:
        _print_result(39, name, False, str(e))
    except Exception as e:
        _print_result(39, name, False, f"{type(e).__name__}: {e}")


def test_40_song_naming_variant():
    """Test 40: Naming Variant — Song of Solomon / Song of Songs"""
    name = "Naming Variant — Song of Solomon vs Song of Songs (JFB)"
    print(f"\n{'─' * 60}\nTest 40: {name}")
    try:
        assert "JFB" in com_mods, "JFB not found in discovered modules"
        cm = CommentaryModule(com_mods["JFB"]["path"])
        try:
            text_solomon = cm.get_section("Song of Solomon 2:1")
            text_songs = cm.get_section("Song of Songs 2:1")

            ok_solomon = (text_solomon is not None
                          and isinstance(text_solomon, str)
                          and len(text_solomon) > 20
                          and not text_solomon.startswith("[No"))
            ok_songs = (text_songs is not None
                        and isinstance(text_songs, str)
                        and len(text_songs) > 20
                        and not text_songs.startswith("[No"))

            print(f"  'Song of Solomon 2:1': {'OK' if ok_solomon else 'FAIL'} "
                  f"({len(text_solomon) if text_solomon and isinstance(text_solomon, str) else 0} chars)")
            if ok_solomon:
                print(f"    Preview: {text_solomon[:100]}")
            else:
                print(f"    Result: {text_solomon}")

            print(f"  'Song of Songs 2:1':    {'OK' if ok_songs else 'FAIL'} "
                  f"({len(text_songs) if text_songs and isinstance(text_songs, str) else 0} chars)")
            if ok_songs:
                print(f"    Preview: {text_songs[:100]}")
            else:
                print(f"    Result: {text_songs}")

            assert ok_solomon and ok_songs, \
                f"Song of Solomon={ok_solomon}, Song of Songs={ok_songs}"
        finally:
            cm.close()

        _print_result(40, name, True)
    except AssertionError as e:
        _print_result(40, name, False, str(e))
    except Exception as e:
        _print_result(40, name, False, f"{type(e).__name__}: {e}")


def test_41_generic_format_module():
    """Test 41: Generic-Format Module — AF Reader or Outlines"""
    name = "Generic-Format Module — list_books + get_section"
    print(f"\n{'─' * 60}\nTest 41: {name}")
    try:
        # Find generic-format modules
        generic_mods = []
        for abbrev, info in sorted(com_mods.items()):
            cm = _load_cm(abbrev)
            if cm is None:
                continue
            fmt = cm.format
            if fmt == "generic":
                generic_mods.append((abbrev, cm))
            else:
                cm.close()

        if not generic_mods:
            print("  No generic-format modules discovered (skipping)")
            _print_result(41, name, True, "No generic modules — nothing to test")
            return

        print(f"  Generic-format modules found: {[g[0] for g in generic_mods]}")

        # Test the first generic module
        abbrev, cm = generic_mods[0]
        try:
            books = cm.list_books()
            print(f"  {abbrev} list_books(): {books[:3]}" +
                  (f" ... +{len(books)-3} more" if len(books) > 3 else ""))

            # Try get_section with numeric section ID
            text = cm.get_section("1")
            assert text is not None, f"{abbrev} get_section('1') returned None"
            assert isinstance(text, str), f"{abbrev} get_section('1') returned non-string"
            assert len(text) > 100, \
                f"{abbrev} section 1 too short ({len(text)} chars)"
            # Basic English prose check: should contain spaces and common words
            assert " " in text, f"{abbrev} section 1 has no spaces (not prose)"
            print(f"  {abbrev} section 1: {len(text)} chars  ✓")
            print(f"    Preview: {text[:120]}")
        finally:
            cm.close()

        _print_result(41, name, True)
    except AssertionError as e:
        _print_result(41, name, False, str(e))
    except Exception as e:
        _print_result(41, name, False, f"{type(e).__name__}: {e}")


def test_42_cross_reference_apparatus():
    """Test 42: Cross-Reference Apparatus — Comprehensive Crossrefs"""
    name = "Cross-Reference Apparatus — verse-reference patterns"
    print(f"\n{'─' * 60}\nTest 42: {name}")
    try:
        # Look for cross-reference modules
        crossref_candidates = [abbrev for abbrev in com_mods
                               if "crossref" in abbrev.lower()
                               or "cross-ref" in abbrev.lower()
                               or abbrev == "Comprehensive Crossrefs"]

        if not crossref_candidates:
            print("  No cross-reference modules found (skipping)")
            _print_result(42, name, True, "No crossref modules — nothing to test")
            return

        print(f"  Cross-ref candidates: {crossref_candidates}")

        found_pattern = False
        for abbrev in crossref_candidates:
            cm = _load_cm(abbrev)
            if cm is None:
                print(f"  {abbrev}: could not load (skipping)")
                continue
            try:
                text = cm.get_section("Matthew 5:3")
                if text is None or not isinstance(text, str):
                    print(f"  {abbrev} Matthew 5:3: no text returned")
                    continue
                print(f"  {abbrev} Matthew 5:3: {len(text)} chars")
                # Check for verse-reference patterns: "Book ch:v", "ch:v"
                ref_patterns = re.findall(
                    r'\b(?:[A-Z]?[a-z]+\s+)?\d+:\d+(?:[–\-]\d+)?\b',
                    text[:2000]
                )
                if ref_patterns:
                    print(f"    Found verse-reference patterns: {ref_patterns[:8]}")
                    found_pattern = True
                else:
                    print("    No verse-reference patterns detected in first 2000 chars")
                    print(f"    Preview: {text[:200]}")
            except Exception as e:
                print(f"  {abbrev}: error — {e}")
            finally:
                cm.close()

        assert found_pattern, \
            "No cross-ref module returned verse-reference patterns for Matthew 5:3"
        _print_result(42, name, True)
    except AssertionError as e:
        _print_result(42, name, False, str(e))
    except Exception as e:
        _print_result(42, name, False, f"{type(e).__name__}: {e}")


def test_43_study_notes_vs_academic():
    """Test 43: Study Notes vs Academic — John 3:16"""
    name = "Study Notes vs Academic — ESV Notes vs Hermeneia NT-21"
    print(f"\n{'─' * 60}\nTest 43: {name}")
    try:
        pair = [("ESV Notes", "Study Notes"), ("Hermeneia NT-21", "Academic")]
        results = {}

        for abbrev, label in pair:
            cm = _load_cm(abbrev)
            if cm is None:
                print(f"  {label} ({abbrev}): not available (skipping)")
                continue
            try:
                text = cm.get_section("John 3:16")
                if text and isinstance(text, str) and not text.startswith("[No"):
                    results[abbrev] = text
                    print(f"  {label} ({abbrev}): {len(text)} chars  ✓")
                    print(f"    Preview: {text[:120]}")
                else:
                    print(f"  {label} ({abbrev}): no text returned — "
                          f"result: {str(text)[:80]}")
            except Exception as e:
                print(f"  {label} ({abbrev}): error — {e}")
            finally:
                cm.close()

        # Both must return text
        missing = [a for a, _ in pair if a not in results]
        if missing:
            assert len(results) >= 1, f"No modules returned text: {missing}"
            print(f"  WARNING: Only {list(results.keys())} returned text "
                  f"(missing: {missing})")
        if len(results) >= 2:
            texts = list(results.values())
            # Scholarly (Hermeneia) should be longer than study notes
            if len(texts[0]) > 0 and len(texts[1]) > 0:
                is_longer = len(texts[1]) > len(texts[0])  # Hermeneia > ESV
                print(f"  Hermeneia ({len(texts[1])} chars) "
                      f"{'longer than' if is_longer else 'NOT longer than'} "
                      f"ESV Notes ({len(texts[0])} chars)")
                # Content should differ (different sources)
                same = texts[0][:200] == texts[1][:200]
                print(f"  Content differs: {'NO (same!)' if same else 'YES (different)'} ✓")
                assert not same, "ESV Notes and Hermeneia return identical content"

        _print_result(43, name, True)
    except AssertionError as e:
        _print_result(43, name, False, str(e))
    except Exception as e:
        _print_result(43, name, False, f"{type(e).__name__}: {e}")


def test_44_concept_search_spanning_books():
    """Test 44: Concept Search Spanning Books — 'covenant' filtered vs unfiltered"""
    name = "Concept Search — 'covenant' book filter (unfiltered vs Hebrews)"
    print(f"\n{'─' * 60}\nTest 44: {name}")
    try:
        # Unfiltered search
        unfiltered = si.search("covenant", source_type="commentary", max_results=30)
        assert len(unfiltered) > 0, "No unfiltered results for 'covenant'"

        # Extract distinct books from source_id (format: "Book ch:v" or "Book ch:v-v")
        unf_books = set()
        for r in unfiltered:
            sid = r[2]
            # source_id for commentaries is like "Genesis 9:9-13" or "Hebrews 8:6"
            # Sometimes it's a generic format name like "People & Places §42"
            ref_match = re.match(r'^([A-Z][A-Za-z0-9]*(?:\s+[A-Z][A-Za-z0-9]+)*)\s+\d+', sid)
            if ref_match:
                unf_books.add(ref_match.group(1))
            elif "§" not in sid:
                # Try anyway
                parts = sid.split()
                if parts and parts[0][0].isupper():
                    unf_books.add(parts[0])

        print(f"  Unfiltered 'covenant': {len(unfiltered)} results "
              f"from {len(unf_books)} books: {sorted(unf_books)[:12]}")
        assert len(unf_books) >= 3, \
            f"Unfiltered 'covenant' covers only {len(unf_books)} distinct books (need ≥ 3)"

        # Filtered search: book="Hebrews"
        filtered = si.search("covenant", source_type="commentary",
                             max_results=20, book="Hebrews")
        print(f"  Filtered (book=Hebrews): {len(filtered)} results")

        # Verify filtered results are only from Hebrews
        if filtered:
            non_hebrews = []
            for r in filtered:
                sid = r[2]
                ref_match = re.match(r'^([A-Z][A-Za-z0-9]*(?:\s+[A-Z][A-Za-z0-9]+)*)\s+\d+', sid)
                book_name = ref_match.group(1) if ref_match else sid.split()[0] if sid else ""
                if "Hebrews" not in book_name and "Heb" not in book_name[:4]:
                    non_hebrews.append((book_name, sid[:50]))
            if non_hebrews:
                print(f"  WARNING: Non-Hebrews results in filtered set: {non_hebrews[:3]}")
            else:
                print("  All filtered results are from Hebrews  ✓")
        else:
            print("  No filtered results — book index may not contain Hebrews entries")

        _print_result(44, name, True)
    except AssertionError as e:
        _print_result(44, name, False, str(e))
    except Exception as e:
        _print_result(44, name, False, f"{type(e).__name__}: {e}")


# ═══════════════════════════════════════════════════════════════════════════
# Main runner
# ═══════════════════════════════════════════════════════════════════════════

def run_all():
    """Run all 12 test cases (33-44) and print summary."""
    tests = [
        test_33_three_tier_romans,
        test_34_obscure_book_coverage,
        test_35_topic_search_redemption,
        test_36_fts5_full_text,
        test_37_browse_chapter_dive_verse,
        test_38_tier_ranking,
        test_39_jude_commentary,
        test_40_song_naming_variant,
        test_41_generic_format_module,
        test_42_cross_reference_apparatus,
        test_43_study_notes_vs_academic,
        test_44_concept_search_spanning_books,
    ]

    for test_func in tests:
        try:
            test_func()
        except Exception as e:
            num = test_func.__name__.split("_")[1] if "_" in test_func.__name__ else "?"
            print(f"\n  [ERROR] Unhandled exception in test {num}:")
            print(f"    {type(e).__name__}: {e}")
            global FAIL
            FAIL += 1

    # ── Summary ──
    total = PASS + FAIL
    print("\n")
    print("=" * 70)
    print("TEST SUMMARY (Tests 33-44)")
    print("=" * 70)
    print()
    print(f"  Total tests:    {total}")
    print(f"  Passed:         {PASS}")
    print(f"  Failed:         {FAIL}")
    print()

    if FAIL > 0:
        print(f"  {FAIL} test(s) failed — review output above for details.")
    else:
        print(f"  All {PASS} tests passed.")

    print()
    print("=" * 70)

    return FAIL == 0


if __name__ == "__main__":
    print("=" * 70)
    print("ACCORDANCE PORT — COMMENTARY & REFERENCE TESTS (33-44)")
    print("=" * 70)
    print()
    print("[SETUP] Calling shared setup from test_search_cases...")
    tsc.setup()
    _refresh_globals()
    print(f"[SETUP]   {len(mods)} total, {len(lex)} lexicons, {len(com_mods)} commentaries")
    print("[SETUP] Ready.\n")

    try:
        success = run_all()
    finally:
        tsc.teardown()

    sys.exit(0 if success else 1)
