"""Tests for session 75 study tools — morphology, word study, translation comparison,
and verse retrieval across doctrinally significant passages.

Scenarios:
  Priest #5  — Confession/Reconciliation (John 20:23)
  Priest #7  — Immaculate Conception (Luke 1:28)
  Priest #12 — Marriage Prep (Tobit 8:4-8, John 2, 1 Cor 13)
  Theologian #2  — Isaiah 6:9-10 in NT (LXX vs MT)
  Theologian #4  — διαθήκη Diachronic Word Study
  Theologian #5  — Synoptic Apocalypse
  Theologian #7  — Matthew 6:13b Doxology (Textual Criticism)
  Theologian #9  — John 19:30 παρέδωκεν
  Theologian #10 — Romans 5:1 ἔχομεν vs ἔχωμεν
  Lay #3   — Wedding at Cana (John 2:1-11)
  Lay #5   — Eucharist: Eat My Flesh (John 6:53-56)
  Lay #10  — Full of Grace (Luke 1:28)
  Lay #11  — Faith Alone (James 2:24)
"""
from __future__ import annotations

import pytest


# =============================================================================
# IMPORT GUARDS
# =============================================================================

# --- Canon ---
try:
    from accordance_parser.canon import (
        parse_reference,
    )
    _has_canon = True
except Exception:
    _has_canon = False

# --- Bible API ---
try:
    from accordance_parser.bible_api import get_verse, get_verse_range
    _has_bible_api = True
except Exception:
    _has_bible_api = False

# --- Morphology ---
try:
    from accordance_parser.morphology import (
        get_morphology, describe_robinson,
    )
    _has_morphology = True
except Exception:
    _has_morphology = False

# --- Lectionary ---
try:
    from accordance_parser.lectionary import get_lectionary_for_date_string
    _has_lectionary = True
except Exception:
    _has_lectionary = False

# --- MCP server tools ---
_has_mcp_server = False
_mcp_server_import_error = ""
try:
    from accordance_mcp_server import (
        get_nabre_verse,
        compare_catholic_translations,
        compare_translations,
    )
    _has_mcp_server = True
except Exception as e:
    _mcp_server_import_error = str(e)

# --- Network detection ---
_has_network = False
try:
    from accordance_parser.bible_api import get_verse as _gv
    r = _gv("Genesis 1:1", "KJV")
    if r and r.text:
        _has_network = True
except Exception:
    pass


# =============================================================================
# HELPERS
# =============================================================================

def _no_traceback(text: str) -> bool:
    """Ensure the text does not contain a Python traceback."""
    return "Traceback (most recent call last)" not in text


# =============================================================================
# PRIEST #5 — CONFESSION / RECONCILIATION (John 20:23)
# =============================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon not available")
class TestPriest5ConfessionReconciliation:
    """Priest #5: Confession/Reconciliation — John 20:23 Greek morphology."""

    def test_parse_john_20_23(self):
        """John 20:23 parses in Catholic canon."""
        r = parse_reference("John 20:23", "catholic")
        assert r is not None
        assert r.book_osis == "John"
        assert r.chapter == 20
        assert r.verse == 23

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_john_20_23_aorist_subjunctive(self):
        """John 20:23 contains ἀφῆτε — aorist active subjunctive 2nd plural."""
        entries = get_morphology("John 20:23")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for John 20:23"
        # Look for aorist subjunctive form
        found_aorist_subj = any(
            "Aorist" in e.morph_description and "Subjunctive" in e.morph_description
            for e in entries if e.morph_code.startswith("V")
        )
        assert found_aorist_subj, (
            f"Expected aorist subjunctive verb (ἀφῆτε) in John 20:23. "
            f"Found codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_john_20_23_perfect_passive(self):
        """John 20:23 contains κεκράτηνται — perfect passive indicative."""
        entries = get_morphology("John 20:23")
        assert isinstance(entries, list)
        assert len(entries) > 0
        # Look for perfect passive
        found_perf_pass = any(
            "Perfect" in e.morph_description and "Passive" in e.morph_description
            for e in entries if e.morph_code.startswith("V")
        )
        assert found_perf_pass, (
            f"Expected perfect passive verb (κεκράτηνται) in John 20:23. "
            f"Found codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_robinson_aorist_active_indicative_3s(self):
        """describe_robinson('V-AAI-3S') → Aorist Active Indicative 3rd Singular."""
        desc = describe_robinson("V-AAI-3S")
        assert "Aorist" in desc
        assert "Active" in desc
        assert "Indicative" in desc
        assert "3rd" in desc or "Third" in desc

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_robinson_perfect_passive_indicative_3p(self):
        """describe_robinson('V-RPI-3P') → Perfect Passive Indicative 3rd Plural."""
        desc = describe_robinson("V-RPI-3P")
        assert isinstance(desc, str) and len(desc) > 0
        assert "Perfect" in desc
        assert "Passive" in desc

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_john_20_22_23(self):
        """John 20:22-23 NABRE returns the breathing-and-forgiveness text."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("John 20:22-23")
        assert isinstance(result, str) and len(result) > 10
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["spirit", "forgive", "sins", "breath"]), (
            f"Missing forgiveness language in John 20:22-23: {result[:200]}"
        )


# =============================================================================
# PRIEST #7 — IMMACULATE CONCEPTION (Luke 1:28)
# =============================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon not available")
class TestPriest7ImmaculateConception:
    """Priest #7: Immaculate Conception — Luke 1:28 κεχαριτωμένη."""

    def test_parse_luke_1_28(self):
        """Luke 1:28 parses in Catholic canon."""
        r = parse_reference("Luke 1:28", "catholic")
        assert r is not None
        assert r.book_osis == "Luke"
        assert r.chapter == 1
        assert r.verse == 28

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_luke_1_28_kecharitomene(self):
        """Luke 1:28 κεχαριτωμένη parses as perfect passive participle."""
        entries = get_morphology("Luke 1:28")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for Luke 1:28"
        # Look for perfect passive participle
        found_ppp = any(
            "Perfect" in e.morph_description
            and "Passive" in e.morph_description
            and "Participle" in e.morph_description
            for e in entries if e.morph_code.startswith("V")
        )
        assert found_ppp, (
            f"Expected perfect passive participle (κεχαριτωμένη) in Luke 1:28. "
            f"Found verb codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_luke_1_28(self):
        """compare_catholic_translations('Luke 1:28') — NABRE vs NRSVCE vs DRB."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("Luke 1:28")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "NRSVCE" in result
        assert "DRB" in result
        # NABRE typically renders "full of grace", NRSVCE "favored one"
        text_lower = result.lower()
        assert any(phrase in text_lower for phrase in ["grace", "favour", "favor", "favoured", "favored"]), (
            f"Missing grace/favor language in Luke 1:28 comparison: {result[:300]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_luke_1_26_38(self):
        """Luke 1:26-38 Annunciation passage retrieves."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Luke 1:26-38")
        assert isinstance(result, str) and len(result) > 50
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["gabriel", "mary", "holy spirit"]), (
            f"Missing Annunciation content in Luke 1:26-38: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_lectionary, reason="Lectionary not available")
    def test_lectionary_dec_8_2026(self):
        """Dec 8, 2026 (Immaculate Conception) has a lectionary entry."""
        result = get_lectionary_for_date_string("2026-12-08")
        assert result is not None, "Expected non-None result for Dec 8, 2026"
        season, readings = result
        assert isinstance(season, str)
        assert isinstance(readings, dict)
        # Fallback dates still return structured results with keys
        for key in ["first", "psalm", "gospel"]:
            assert key in readings, f"Missing key {key} in Dec 8 lectionary"


# =============================================================================
# PRIEST #12 — MARRIAGE PREP (Tobit 8:4-8, John 2, 1 Cor 13)
# =============================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon not available")
class TestPriest12MarriagePrep:
    """Priest #12: Marriage Prep — deuterocanonical Tobit, Cana, 1 Cor 13."""

    def test_parse_tobit_8_4(self):
        """Tobit 8:4 parses in Catholic canon (deuterocanonical)."""
        r = parse_reference("Tobit 8:4", "catholic")
        assert r is not None
        assert r.book_osis == "Tob"

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_tobit_8_4_8(self):
        """Tobit 8:4-8 (wedding prayer of Tobias and Sarah) retrieves."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Tobit 8:4-8")
        assert isinstance(result, str) and len(result) > 10
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["pray", "marriage", "bless", "mercy", "sarah"]), (
            f"Missing marriage/wedding prayer content in Tobit 8:4-8: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_john_2_1_11_cana(self):
        """John 2:1-11 (Wedding at Cana) retrieves full text."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("John 2:1-11")
        assert isinstance(result, str) and len(result) > 20
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["wedding", "cana", "wine", "water", "miracle"]), (
            f"Missing Wedding at Cana content in John 2:1-11: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_1_corinthians_13_4_8(self):
        """1 Corinthians 13:4-8 (love passage) retrieves."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("1 Corinthians 13:4-8")
        assert isinstance(result, str) and len(result) > 20
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["love", "patient", "kind"]), (
            f"Missing love passage content in 1 Cor 13:4-8: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_john_2_4_vocative(self):
        """John 2:4 γύναι — vocative case for 'woman' (not disrespectful)."""
        entries = get_morphology("John 2:4")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for John 2:4"
        # Look for vocative noun
        found_vocative = any(
            "Vocative" in e.morph_description
            for e in entries if e.morph_code.startswith("N")
        )
        assert found_vocative, (
            f"Expected vocative noun (γύναι) in John 2:4. "
            f"Found noun codes: {[e.morph_code for e in entries if e.morph_code.startswith('N')]}"
        )


# =============================================================================
# THEOLOGIAN #2 — ISAIAH 6:9-10 IN NT (LXX vs MT)
# =============================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon not available")
class TestTheologian2Isaiah69Minus10:
    """Theologian #2: Isaiah 6:9-10 in NT — LXX vs MT citation."""

    def test_parse_isaiah_6_9_10(self):
        """Isaiah 6:9-10 parses in Catholic canon."""
        r = parse_reference("Isaiah 6:9-10", "catholic")
        assert r is not None
        assert r.book_osis == "Isa"
        assert r.chapter == 6
        assert r.verse == 9
        assert r.end_verse == 10

    def test_parse_matthew_13_14_15(self):
        """Matthew 13:14-15 (NT citation of Isaiah 6:9-10) parses."""
        r = parse_reference("Matthew 13:14-15", "catholic")
        assert r is not None
        assert r.book_osis == "Matt"
        assert r.chapter == 13

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_kjv_isaiah_6_9_10(self):
        """Isaiah 6:9-10 KJV retrieves the commission text."""
        r = get_verse("Isaiah 6:9-10", "KJV")
        assert r is not None and r.text
        text_lower = r.text.lower()
        assert any(term in text_lower for term in ["hear", "understand", "see", "perceive"]), (
            f"Missing hearing/seeing content in Isaiah 6:9-10 KJV: {r.text[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_kjv_matthew_13_14_15(self):
        """Matthew 13:14-15 KJV — NT citation of Isaiah."""
        r = get_verse("Matthew 13:14-15", "KJV")
        assert r is not None and r.text
        text_lower = r.text.lower()
        assert any(term in text_lower for term in ["hear", "esaias", "isaiah", "prophecy"]), (
            f"Missing Isaiah citation in Matthew 13:14-15 KJV: {r.text[:200]}"
        )


# =============================================================================
# THEOLOGIAN #4 — διαθήκη DIACHRONIC WORD STUDY
# =============================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon not available")
class TestTheologian4Diatheke:
    """Theologian #4: διαθήκη — Covenant/Testament diachronic word study."""

    def test_parse_genesis_15_18(self):
        """Genesis 15:18 (covenant with Abram) parses."""
        r = parse_reference("Genesis 15:18", "catholic")
        assert r is not None
        assert r.book_osis == "Gen"

    def test_parse_jeremiah_31_31(self):
        """Jeremiah 31:31 (New Covenant promise) parses."""
        r = parse_reference("Jeremiah 31:31", "catholic")
        assert r is not None
        assert r.book_osis == "Jer"

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_kjv_hebrews_9_15_17(self):
        """Hebrews 9:15-17 KJV — covenant/testament language."""
        r = get_verse("Hebrews 9:15-17", "KJV")
        assert r is not None and r.text
        text_lower = r.text.lower()
        assert any(term in text_lower for term in ["covenant", "testament", "mediator"]), (
            f"Missing covenant/testament language in Hebrews 9:15-17 KJV: {r.text[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_hebrews_9_16_17(self):
        """compare_catholic_translations('Hebrews 9:16-17') — testament vs covenant."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("Hebrews 9:16-17")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "DRB" in result
        text_lower = result.lower()
        assert any(term in text_lower for term in ["testament", "covenant"]), (
            f"Missing testament/covenant language in Hebrews 9:16-17: {result[:300]}"
        )


# =============================================================================
# THEOLOGIAN #5 — SYNOPTIC APOCALYPSE
# =============================================================================

class TestTheologian5SynopticApocalypse:
    """Theologian #5: Synoptic Apocalypse — Matthew 24, Mark 13, Luke 21."""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_matthew_24_15(self):
        """Matthew 24:15 retrieves — 'abomination of desolation' in Matthew."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Matthew 24:15")
        assert isinstance(result, str) and len(result) > 10
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["desolation", "desolating", "abomination"]), (
            f"Missing desolation language in Matthew 24:15: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_mark_13_14(self):
        """Mark 13:14 retrieves — parallel 'abomination of desolation'."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Mark 13:14")
        assert isinstance(result, str) and len(result) > 10
        assert _no_traceback(result)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_luke_21_20(self):
        """Luke 21:20 retrieves — parallel passage with different language."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Luke 21:20")
        assert isinstance(result, str) and len(result) > 10
        assert _no_traceback(result)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_verse_range_matthew_24_15_20(self):
        """get_verse_range('Matthew 24:15-20') returns verses 15 through 20."""
        results = get_verse_range("Matthew 24:15-20")
        assert isinstance(results, list)
        assert len(results) >= 3, (
            f"Expected at least 3 verses in Matthew 24:15-20, got {len(results)}"
        )
        # Each result should have verse number and text
        for r in results:
            assert hasattr(r, "verse")
            assert hasattr(r, "text")
            assert len(r.text) > 0

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_kjv_mark_13_14_abomination(self):
        """Mark 13:14 KJV contains 'abomination of desolation'."""
        r = get_verse("Mark 13:14", "KJV")
        assert r is not None and r.text
        text_lower = r.text.lower()
        assert "abomination" in text_lower and "desolation" in text_lower, (
            f"Missing 'abomination of desolation' in Mark 13:14 KJV: {r.text[:200]}"
        )


# =============================================================================
# THEOLOGIAN #7 — MATTHEW 6:13b DOXOLOGY (TEXTUAL CRITICISM)
# =============================================================================

class TestTheologian7Doxology:
    """Theologian #7: Matthew 6:13b — Doxology textual criticism."""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_kjv_matthew_6_13_doxology(self):
        """Matthew 6:13 KJV includes the doxology ('For thine is the kingdom...')."""
        r = get_verse("Matthew 6:13", "KJV")
        assert r is not None and r.text
        text_lower = r.text.lower()
        assert any(phrase in text_lower for phrase in ["kingdom", "power", "glory"]), (
            f"Missing doxology in Matthew 6:13 KJV: {r.text[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_matthew_6_13(self):
        """compare_catholic_translations('Matthew 6:13') — DRB includes doxology, NABRE may omit."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("Matthew 6:13")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "NRSVCE" in result
        assert "DRB" in result

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_verse_text_drb_matthew_6_13(self):
        """Matthew 6:13 DRB (Douay-Rheims, following Vulgate) includes doxology."""
        r = get_verse("Matthew 6:13", "DRB")
        assert r is not None and r.text
        text_lower = r.text.lower()
        # DRB follows Vulgate — should include doxology
        assert any(phrase in text_lower for phrase in ["kingdom", "power", "glory", "amen"]), (
            f"DRB should include doxology in Matthew 6:13: {r.text[:200]}"
        )


# =============================================================================
# THEOLOGIAN #9 — JOHN 19:30 παρέδωκεν
# =============================================================================

@pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
class TestTheologian9Paredoken:
    """Theologian #9: John 19:30 παρέδωκεν — 'handed over / gave up.'"""

    def test_morphology_john_19_30_aorist(self):
        """John 19:30 παρέδωκεν parses as aorist active indicative 3rd singular."""
        entries = get_morphology("John 19:30")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for John 19:30"
        # Look for V-AAI-3S (aorist active indicative 3rd singular)
        found_aai_3s = any(
            e.morph_code == "V-AAI-3S"
            for e in entries
        )
        assert found_aai_3s, (
            f"Expected V-AAI-3S (παρέδωκεν) in John 19:30. "
            f"Found verb codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    def test_robinson_v_aai_3s(self):
        """describe_robinson('V-AAI-3S') → Verb - Aorist Active Indicative 3rd Singular."""
        desc = describe_robinson("V-AAI-3S")
        assert "Verb" in desc or "Aorist" in desc
        assert "Active" in desc
        assert "Indicative" in desc
        assert ("3rd" in desc or "Third" in desc) and ("Singular" in desc or "Sing" in desc)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_john_19_30(self):
        """compare_catholic_translations('John 19:30') — handed over / gave up."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("John 19:30")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "DRB" in result


# =============================================================================
# THEOLOGIAN #10 — ROMANS 5:1 ἔχομεν vs ἔχωμεν
# =============================================================================

@pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
class TestTheologian10Romans5mut1:
    """Theologian #10: Romans 5:1 — ἔχομεν (indicative) vs ἔχωμεν (subjunctive)."""

    def test_morphology_romans_5_1_present_indicative(self):
        """Romans 5:1 verb parses as present active indicative 1st plural."""
        entries = get_morphology("Romans 5:1")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for Romans 5:1"
        # Look for PAI-1P (present active indicative 1st plural)
        found_pai_1p = any(
            e.morph_code == "V-PAI-1P" or (
                "Present" in e.morph_description
                and "Active" in e.morph_description
                and "Indicative" in e.morph_description
                and "1st" in e.morph_description
                and "Plural" in e.morph_description
            )
            for e in entries if e.morph_code.startswith("V")
        )
        assert found_pai_1p, (
            f"Expected V-PAI-1P (ἔχομεν) in Romans 5:1. "
            f"Found verb codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    def test_robinson_v_pai_1p(self):
        """describe_robinson('V-PAI-1P') → Present Active Indicative 1st Plural."""
        desc = describe_robinson("V-PAI-1P")
        assert "Present" in desc
        assert "Active" in desc
        assert "Indicative" in desc
        assert ("1st" in desc or "First" in desc) and ("Plural" in desc or "Pl" in desc)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_translations_romans_5_1(self):
        """compare_translations('Romans 5:1', 'KJV,ESV,NASB') — all render 'we have peace' (indicative)."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_translations("Romans 5:1", translations="KJV,ESV,NASB")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        text_lower = result.lower()
        assert "peace" in text_lower, f"Missing 'peace' in Romans 5:1 comparison: {result[:300]}"
        # All three should render as indicative ("we have peace"), not subjunctive
        assert "we have" in text_lower or "have peace" in text_lower, (
            f"Expected indicative 'we have peace' in Romans 5:1: {result[:300]}"
        )


# =============================================================================
# LAY #3 — WEDDING AT CANA (John 2:1-11)
# =============================================================================

class TestLay3WeddingCana:
    """Lay #3: Wedding at Cana — John 2:1-11 with morphology."""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_john_2_1_11_full_text(self):
        """John 2:1-11 NABRE returns the full Wedding at Cana narrative."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        try:
            result = get_nabre_verse("John 2:1-11")
        except Exception as e:
            pytest.skip(f"MCP server call failed: {e}")
        assert isinstance(result, str) and len(result) > 20
        assert _no_traceback(result)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_john_2_4_woman(self):
        """John 2:4 NABRE contains 'Woman' (γύναι — vocative, not disrespectful)."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("John 2:4")
        assert isinstance(result, str) and len(result) > 5
        assert _no_traceback(result)
        assert "woman" in result.lower(), (
            f"Expected 'Woman' (γύναι) in John 2:4 NABRE: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_john_2_4_vocative_case(self):
        """John 2:4 γύναι has vocative case morphology."""
        entries = get_morphology("John 2:4")
        assert isinstance(entries, list)
        assert len(entries) > 0
        found_vocative = any(
            "Vocative" in e.morph_description
            for e in entries if e.morph_code.startswith("N")
        )
        assert found_vocative, (
            f"Expected vocative noun (γύναι) in John 2:4. "
            f"Found noun codes: {[e.morph_code for e in entries if e.morph_code.startswith('N')]}"
        )


# =============================================================================
# LAY #5 — EUCHARIST: EAT MY FLESH (John 6:53-56)
# =============================================================================

class TestLay5Eucharist:
    """Lay #5: Eucharist — John 6:53-56 'Eat My Flesh, Drink My Blood.'"""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_john_6_53_56(self):
        """John 6:53-56 NABRE retrieves the Eucharistic discourse climax."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("John 6:53-56")
        assert isinstance(result, str) and len(result) > 20
        assert _no_traceback(result)
        text_lower = result.lower()
        assert "flesh" in text_lower and "blood" in text_lower, (
            f"Missing flesh/blood in John 6:53-56: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_john_6_53_56(self):
        """compare_catholic_translations('John 6:53-56') — NABRE, NRSVCE, DRB."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("John 6:53-56")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "NRSVCE" in result
        assert "DRB" in result

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_eucharist_literal_rendering(self):
        """All three Catholic translations render 'eat the flesh...drink my blood' literally."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("John 6:53-56")
        assert isinstance(result, str) and len(result) > 0
        text_lower = result.lower()
        assert "flesh" in text_lower, f"Missing 'flesh' in John 6:53-56: {result[:300]}"
        assert "blood" in text_lower, f"Missing 'blood' in John 6:53-56: {result[:300]}"
        # All three translations render literally (not metaphorical "my body")
        assert "eat" in text_lower or "drink" in text_lower, (
            f"Expected literal 'eat/drink' language: {result[:300]}"
        )


# =============================================================================
# LAY #10 — FULL OF GRACE (Luke 1:28)
# =============================================================================

class TestLay10FullOfGrace:
    """Lay #10: Full of Grace — Luke 1:28 comparison and morphology."""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_luke_1_28_grace(self):
        """compare_catholic_translations('Luke 1:28') — NABRE 'full of grace' vs NRSVCE 'favored one.'"""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("Luke 1:28")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        text_lower = result.lower()
        # NABRE uses "full of grace"; NRSVCE uses "favored one" / "highly favoured"
        assert any(phrase in text_lower for phrase in ["grace", "favour", "favor", "favoured", "favored"]), (
            f"Missing grace/favor language in Luke 1:28 comparison: {result[:300]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_luke_1_28_perfect_passive_participle(self):
        """Luke 1:28 κεχαριτωμένη parses as perfect passive participle (XPP)."""
        entries = get_morphology("Luke 1:28")
        assert isinstance(entries, list)
        assert len(entries) > 0
        found_xpp = any(
            "XPP" in e.morph_code
            or (
                "Perfect" in e.morph_description
                and "Passive" in e.morph_description
                and "Participle" in e.morph_description
            )
            for e in entries if e.morph_code.startswith("V")
        )
        assert found_xpp, (
            f"Expected perfect passive participle (XPP) κεχαριτωμένη in Luke 1:28. "
            f"Found verb codes: {[e.morph_code for e in entries if e.morph_code.startswith('V')]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_luke_1_28_text(self):
        """Luke 1:28 NABRE retrieves the 'Hail, full of grace' text."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("Luke 1:28")
        assert isinstance(result, str) and len(result) > 5
        assert _no_traceback(result)
        text_lower = result.lower()
        assert any(term in text_lower for term in ["hail", "grace", "favored", "lord"]), (
            f"Missing greeting content in Luke 1:28 NABRE: {result[:200]}"
        )


# =============================================================================
# LAY #11 — FAITH ALONE (James 2:24)
# =============================================================================

class TestLay11FaithAlone:
    """Lay #11: Faith Alone — James 2:24 'not by faith alone.'"""

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_nabre_james_2_24(self):
        """James 2:24 NABRE retrieves."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("James 2:24")
        assert isinstance(result, str) and len(result) > 5
        assert _no_traceback(result)

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_james_2_24_not_faith_alone(self):
        """James 2:24 text contains 'not by faith alone.'"""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = get_nabre_verse("James 2:24")
        text_lower = result.lower()
        # Key Catholic prooftext: "not by faith alone"
        assert "faith" in text_lower, f"Missing 'faith' in James 2:24: {result[:200]}"
        assert "alone" in text_lower or "only" in text_lower, (
            f"Expected 'not by faith alone/only' in James 2:24: {result[:200]}"
        )

    @pytest.mark.skipif(not _has_network, reason="Requires network")
    def test_compare_catholic_james_2_24(self):
        """compare_catholic_translations('James 2:24') — all Catholic translations agree."""
        if not _has_mcp_server:
            pytest.skip("MCP server tools not available")
        result = compare_catholic_translations("James 2:24")
        assert isinstance(result, str) and len(result) > 0
        assert _no_traceback(result)
        assert "NABRE" in result
        assert "NRSVCE" in result
        assert "DRB" in result
        text_lower = result.lower()
        assert "faith" in text_lower, f"Missing 'faith' in James 2:24 comparison: {result[:300]}"
        assert "alone" in text_lower or "only" in text_lower, (
            f"Missing 'alone/only' in James 2:24 comparison: {result[:300]}"
        )

    @pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
    def test_morphology_james_2_24_justified(self):
        """James 2:24 δικαιοῦται ('is justified') parses correctly."""
        entries = get_morphology("James 2:24")
        assert isinstance(entries, list)
        assert len(entries) > 0, "Expected morphology entries for James 2:24"
        # Look for present passive indicative 3rd singular (V-PPI-3S) or similar
        found_justified = any(
            "justif" in e.lemma.lower()
            or "δικαι" in e.word.lower()
            or "justif" in e.morph_description.lower()
            for e in entries
        )
        # Even if lemma lookup fails, there should be verb morphology
        has_verbs = any(e.morph_code.startswith("V") for e in entries)
        assert has_verbs or found_justified, (
            f"Expected verb morphology for δικαιοῦται in James 2:24. "
            f"All entries: {[(e.word[:20], e.morph_code) for e in entries]}"
        )


# =============================================================================
# END
# =============================================================================
