"""Tests for Catholic lectionary and sacramental theology chains."""
from __future__ import annotations
import pytest

# --- Lectionary ---
try:
    from accordance_parser.lectionary import (
        get_lectionary_readings, get_lectionary_for_date_string, _LECTIONARY,
    )
    _has_lectionary = True
except Exception:
    _has_lectionary = False

# --- 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
    _has_bible_api = True
except Exception:
    _has_bible_api = False

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

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


# =====================================================================
# LOCAL TESTS — Lectionary date handling
# =====================================================================

@pytest.mark.skipif(not _has_lectionary, reason="Lectionary module not available")
class TestLectionaryDateHandling:
    """S-P-1, S-P-5, S-P-7, S-L-1: Daily Mass reading date handling."""

    def test_lectionary_has_entries(self):
        """The hardcoded lectionary should have entries."""
        assert isinstance(_LECTIONARY, dict)
        assert len(_LECTIONARY) > 0

    def test_lectionary_valid_date_may25(self):
        """May 25, 2026 — Monday after Pentecost."""
        result = get_lectionary_for_date_string("2026-05-25")
        assert result is not None
        title, readings = result
        assert "Pentecost" in title or "Monday" in title
        assert "first" in readings
        assert "psalm" in readings or "gospel" in readings

    def test_lectionary_valid_date_may26(self):
        """May 26, 2026 — Tuesday after Pentecost."""
        result = get_lectionary_for_date_string("2026-05-26")
        assert result is not None
        title, readings = result
        assert len(title) > 0
        assert "first" in readings

    def test_lectionary_valid_date_may31(self):
        """May 31, 2026 — Trinity Sunday or Visitation."""
        result = get_lectionary_for_date_string("2026-05-31")
        # May or may not be in hardcoded lectionary
        if result is not None:
            title, readings = result
            assert len(title) > 0

    def test_lectionary_invalid_format(self):
        """Invalid date format should raise or return None."""
        try:
            result = get_lectionary_for_date_string("not-a-date")
            # If it doesn't raise, should return None
            assert result is None
        except (ValueError, Exception):
            pass  # raising is also acceptable

    def test_lectionary_empty_string(self):
        """Empty string should not crash."""
        try:
            result = get_lectionary_for_date_string("")
            assert result is None
        except (ValueError, Exception):
            pass

    def test_lectionary_future_date(self):
        """Date not in hardcoded range should return None or gracefully handle."""
        result = get_lectionary_for_date_string("2026-12-25")
        # May return None or fallback — both are acceptable
        assert result is None or isinstance(result, tuple)

    def test_lectionary_past_date(self):
        result = get_lectionary_for_date_string("2020-01-01")
        assert result is None or isinstance(result, tuple)

    def test_lectionary_readings_have_correct_structure(self):
        """Each reading should have title + dict of readings."""
        for date_str, (title, readings) in _LECTIONARY.items():
            assert isinstance(title, str)
            assert len(title) > 0
            assert isinstance(readings, dict)
            assert len(readings) >= 2

    def test_lectionary_readings_have_colon_or_ref(self):
        """Gospel readings should contain verse references (basic check)."""
        for date_str, (title, readings) in _LECTIONARY.items():
            if "gospel" in readings:
                ref = readings["gospel"]
                assert ":" in ref or len(ref) > 0

    def test_get_lectionary_readings_no_date(self):
        """get_lectionary_readings should accept a date or return today."""
        import datetime
        try:
            result = get_lectionary_readings(target_date=datetime.date.today())
        except TypeError:
            result = get_lectionary_readings()
        if result is not None:
            title, readings = result
            assert len(title) > 0
            assert isinstance(readings, dict)
            assert len(readings) >= 2

    def test_lectionary_readings_contain_verse_refs(self):
        """Gospel readings should contain verse references."""
        for date_str, (title, readings) in _LECTIONARY.items():
            if "gospel" in readings:
                ref = readings["gospel"]
                # Should look like "John 3:16-18" — contains a digit after a colon
                assert ":" in ref or len(ref) > 0



# =====================================================================
# LOCAL TESTS — Reference parsing for sacramental passages
# =====================================================================

@pytest.mark.skipif(not _has_canon, reason="Canon module not available")
class TestSacramentalReferenceParsing:
    """S-P-2, S-L-5, S-L-7: Reference parsing for Eucharist and Confession."""

    def test_parse_john_6_53(self):
        r = parse_reference("John 6:53", "catholic")
        assert r is not None
        assert r.book_osis == "John"
        assert r.chapter == 6
        assert r.verse == 53

    def test_parse_john_6_54(self):
        r = parse_reference("John 6:54", "catholic")
        assert r is not None
        assert r.book_osis == "John"

    def test_parse_john_20_23(self):
        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

    def test_parse_james_5_16(self):
        r = parse_reference("James 5:16", "catholic")
        assert r is not None
        assert r.book_osis == "Jas"

    def test_parse_exodus_34_6(self):
        r = parse_reference("Exodus 34:6", "catholic")
        assert r is not None
        assert r.book_osis == "Exod"

    def test_parse_psalm_51(self):
        r = parse_reference("Psalms 51:4", "catholic")
        assert r is not None
        assert r.book_osis == "Ps"

    def test_eucharist_passages_exist(self):
        """All key Eucharist passages exist in canon."""
        for ref in ["John 6:53", "John 6:54", "Matthew 26:26", "1 Corinthians 11:24"]:
            r = parse_reference(ref, "catholic")
            assert r is not None, f"Failed to parse {ref}"


# =====================================================================
# NETWORK-DEPENDENT TESTS
# =====================================================================

@pytest.mark.skipif(not _has_network, reason="Requires network")
class TestSacramentalVerseRetrieval:
    """S-P-2, S-L-5, S-L-7: Network-based verse retrieval."""

    def test_john_6_53_nabre(self):
        r = get_verse("John 6:53", "NABRE")
        assert r is not None
        assert "flesh" in r.text.lower() or "life" in r.text.lower()

    def test_john_20_23_nabre(self):
        r = get_verse("John 20:23", "NABRE")
        assert r is not None
        assert "forgive" in r.text.lower() or "sins" in r.text.lower()

    def test_james_5_16_nabre(self):
        r = get_verse("James 5:16", "NABRE")
        assert r is not None
        assert "confess" in r.text.lower() or "pray" in r.text.lower()


@pytest.mark.skipif(not _has_morphology, reason="Morphology not available")
class TestMorphologyLocal:
    """S-P-5, S-P-7: Morphology code descriptions (local)."""

    def test_describe_robinson_present_imperative(self):
        desc = describe_robinson("V-PAM-2P")
        assert len(desc) > 0
        assert "Present" in desc or "Imperative" in desc

    def test_describe_robinson_aorist(self):
        desc = describe_robinson("V-AAI-3S")
        assert len(desc) > 0
        assert "Aorist" in desc

    def test_describe_robinson_perfect(self):
        desc = describe_robinson("V-XAI-3S")
        assert len(desc) > 0
        assert "Perfect" in desc

    def test_describe_robinson_present_active_participle(self):
        desc = describe_robinson("V-PAP-NSM")
        assert len(desc) > 0
