"""
Tests for Strong's Concordance functionality.

Tests cover:
- Strong's data loading and lookup
- Hebrew and Greek entry retrieval
- Search functionality
- Pagination
- Web routes
- API endpoints
"""

import pytest
from turboapi.testclient import TestClient

from kjvstudy_org.server import app
from kjvstudy_org.strongs import (
    get_strongs_entry,
    get_strongs_definition,
    get_strongs_word,
    get_strongs_transliteration,
    get_strongs_kjv_usage,
    format_strongs_entry,
    search_strongs,
    get_all_strongs,
)


@pytest.fixture
def client():
    """Create test client"""
    return TestClient(app)


class TestStrongsDataLoading:
    """Tests for Strong's data loading and basic lookup"""

    def test_get_hebrew_entry(self):
        """Test retrieving a Hebrew Strong's entry"""
        entry = get_strongs_entry("H1")
        assert entry is not None
        assert "lemma" in entry

    def test_get_greek_entry(self):
        """Test retrieving a Greek Strong's entry"""
        entry = get_strongs_entry("G1")
        assert entry is not None
        assert "lemma" in entry

    def test_get_entry_case_insensitive(self):
        """Test Strong's lookup is case-insensitive"""
        entry1 = get_strongs_entry("H430")
        entry2 = get_strongs_entry("h430")
        assert entry1 == entry2

    def test_get_entry_with_whitespace(self):
        """Test Strong's lookup handles whitespace"""
        entry = get_strongs_entry("  H1  ")
        assert entry is not None

    def test_get_invalid_entry(self):
        """Test invalid Strong's number returns None"""
        entry = get_strongs_entry("X9999")
        assert entry is None

    def test_get_nonexistent_entry(self):
        """Test nonexistent Strong's number returns None"""
        entry = get_strongs_entry("H99999")
        assert entry is None

    def test_get_empty_entry(self):
        """Test empty string returns None"""
        entry = get_strongs_entry("")
        assert entry is None

    def test_get_none_entry(self):
        """Test None input returns None"""
        entry = get_strongs_entry(None)
        assert entry is None


class TestStrongsHelperFunctions:
    """Tests for Strong's helper functions"""

    def test_get_definition(self):
        """Test getting Strong's definition"""
        definition = get_strongs_definition("G26")
        assert definition is not None
        assert len(definition) > 0

    def test_get_word(self):
        """Test getting original word (lemma)"""
        word = get_strongs_word("G26")
        assert word is not None
        # Greek agape
        assert "γάπ" in word or "agap" in word.lower()

    def test_get_transliteration(self):
        """Test getting transliteration"""
        translit = get_strongs_transliteration("G26")
        assert translit is not None
        # Transliteration is "agápē" - check for base characters
        assert "ag" in translit.lower() and "p" in translit.lower()

    def test_get_kjv_usage(self):
        """Test getting KJV usage"""
        usage = get_strongs_kjv_usage("G26")
        assert usage is not None
        assert "love" in usage.lower()

    def test_get_hebrew_transliteration(self):
        """Test Hebrew transliteration uses xlit field"""
        translit = get_strongs_transliteration("H1")
        assert translit is not None

    def test_helper_functions_invalid_number(self):
        """Test helper functions handle invalid numbers"""
        assert get_strongs_definition("X9999") is None
        assert get_strongs_word("X9999") is None
        assert get_strongs_transliteration("X9999") is None
        assert get_strongs_kjv_usage("X9999") is None


class TestFormatStrongsEntry:
    """Tests for format_strongs_entry function"""

    def test_format_greek_entry(self):
        """Test formatting Greek entry"""
        entry = format_strongs_entry("G26")
        assert entry is not None
        assert entry["strongs"] == "G26"
        assert entry["language"] == "Greek"
        assert "word" in entry
        assert "definition" in entry

    def test_format_hebrew_entry(self):
        """Test formatting Hebrew entry"""
        entry = format_strongs_entry("H430")
        assert entry is not None
        assert entry["strongs"] == "H430"
        assert entry["language"] == "Hebrew"
        assert "word" in entry
        assert "definition" in entry

    def test_format_entry_has_all_fields(self):
        """Test formatted entry has all expected fields"""
        entry = format_strongs_entry("G26")
        expected_fields = [
            "strongs", "language", "word", "transliteration",
            "definition", "kjv_usage", "derivation"
        ]
        for field in expected_fields:
            assert field in entry

    def test_format_hebrew_has_pronunciation(self):
        """Test Hebrew entries include pronunciation"""
        entry = format_strongs_entry("H1")
        assert "pronunciation" in entry

    def test_format_invalid_entry(self):
        """Test formatting invalid entry returns None"""
        entry = format_strongs_entry("X9999")
        assert entry is None


class TestSearchStrongs:
    """Tests for Strong's search functionality"""

    def test_search_finds_results(self):
        """Test search returns results"""
        results = search_strongs("love")
        assert len(results) > 0

    def test_search_result_structure(self):
        """Test search results have correct structure"""
        results = search_strongs("love", limit=1)
        assert len(results) > 0
        result = results[0]
        assert "strongs" in result
        assert "language" in result
        assert "word" in result
        assert "definition" in result

    def test_search_hebrew_only(self):
        """Test search Hebrew only"""
        results = search_strongs("God", language="hebrew")
        for result in results:
            assert result["language"] == "Hebrew"

    def test_search_greek_only(self):
        """Test search Greek only"""
        results = search_strongs("love", language="greek")
        for result in results:
            assert result["language"] == "Greek"

    def test_search_both_languages(self):
        """Test search both languages"""
        results = search_strongs("peace", language="both")
        languages = set(r["language"] for r in results)
        # Should have at least one result
        assert len(results) > 0

    def test_search_respects_limit(self):
        """Test search respects limit parameter"""
        results = search_strongs("the", limit=5)
        assert len(results) <= 5

    def test_search_case_insensitive(self):
        """Test search is case-insensitive"""
        results1 = search_strongs("love")
        results2 = search_strongs("LOVE")
        assert len(results1) == len(results2)

    def test_search_no_results(self):
        """Test search with no results"""
        results = search_strongs("xyznonexistent123")
        assert len(results) == 0


class TestGetAllStrongs:
    """Tests for paginated Strong's listing"""

    def test_get_all_hebrew(self):
        """Test getting all Hebrew entries"""
        data = get_all_strongs("hebrew", page=1, per_page=100)
        assert "entries" in data
        assert "total" in data
        assert "page" in data
        assert "total_pages" in data
        assert len(data["entries"]) == 100
        assert data["total"] > 8000  # Should have ~8674 Hebrew entries

    def test_get_all_greek(self):
        """Test getting all Greek entries"""
        data = get_all_strongs("greek", page=1, per_page=100)
        assert len(data["entries"]) == 100
        assert data["total"] > 5000  # Should have ~5523 Greek entries

    def test_pagination_page_2(self):
        """Test pagination returns different entries"""
        page1 = get_all_strongs("hebrew", page=1, per_page=10)
        page2 = get_all_strongs("hebrew", page=2, per_page=10)

        # Entries should be different
        page1_ids = [e["strongs"] for e in page1["entries"]]
        page2_ids = [e["strongs"] for e in page2["entries"]]
        assert page1_ids != page2_ids

    def test_pagination_total_pages(self):
        """Test total pages calculation"""
        data = get_all_strongs("hebrew", page=1, per_page=100)
        expected_pages = (data["total"] + 99) // 100
        assert data["total_pages"] == expected_pages

    def test_entries_sorted_by_number(self):
        """Test entries are sorted by Strong's number"""
        data = get_all_strongs("hebrew", page=1, per_page=10)
        numbers = [int(e["strongs"][1:]) for e in data["entries"]]
        assert numbers == sorted(numbers)

    def test_invalid_language(self):
        """Test invalid language returns empty"""
        data = get_all_strongs("invalid", page=1)
        assert data["entries"] == []
        assert data["total"] == 0

    def test_entry_has_required_fields(self):
        """Test paginated entries have required fields"""
        data = get_all_strongs("greek", page=1, per_page=1)
        entry = data["entries"][0]
        assert "strongs" in entry
        assert "language" in entry
        assert "word" in entry
        assert "transliteration" in entry
        assert "definition" in entry


class TestStrongsWebRoutes:
    """Tests for Strong's web routes"""

    def test_strongs_index_page(self, client):
        """Test Strong's index page loads"""
        response = client.get("/strongs")
        assert response.status_code == 200
        content = response.content.decode()
        assert "Strong" in content

    def test_strongs_search_page(self, client):
        """Test Strong's search with query"""
        response = client.get("/strongs?q=love")
        assert response.status_code == 200
        content = response.content.decode()
        assert "love" in content.lower()

    def test_strongs_entry_page_greek(self, client):
        """Test Greek Strong's entry page"""
        response = client.get("/strongs/G26")
        assert response.status_code == 200
        content = response.content.decode()
        assert "G26" in content
        assert "Greek" in content

    def test_strongs_entry_page_hebrew(self, client):
        """Test Hebrew Strong's entry page"""
        response = client.get("/strongs/H430")
        assert response.status_code == 200
        content = response.content.decode()
        assert "H430" in content
        assert "Hebrew" in content

    def test_strongs_entry_case_insensitive(self, client):
        """Test entry page handles lowercase"""
        response = client.get("/strongs/g26")
        assert response.status_code == 200

    def test_strongs_invalid_entry(self, client):
        """Test invalid entry returns 404"""
        response = client.get("/strongs/X9999")
        assert response.status_code == 404

    def test_strongs_hebrew_index(self, client):
        """Test Hebrew index page"""
        response = client.get("/strongs/hebrew")
        assert response.status_code == 200
        content = response.content.decode()
        assert "Hebrew" in content
        assert "H1" in content

    def test_strongs_greek_index(self, client):
        """Test Greek index page"""
        response = client.get("/strongs/greek")
        assert response.status_code == 200
        content = response.content.decode()
        assert "Greek" in content
        assert "G1" in content

    def test_strongs_hebrew_pagination(self, client):
        """Test Hebrew index pagination"""
        response = client.get("/strongs/hebrew?page=2")
        assert response.status_code == 200
        content = response.content.decode()
        assert "Page 2" in content

    def test_strongs_greek_pagination(self, client):
        """Test Greek index pagination"""
        response = client.get("/strongs/greek?page=2")
        assert response.status_code == 200

    def test_strongs_entry_has_verse_occurrences(self, client):
        """Test entry page shows verse occurrences"""
        # Use a common word that should have occurrences
        response = client.get("/strongs/G2316")  # theos (God)
        assert response.status_code == 200
        content = response.content.decode()
        # Should have occurrences section if interlinear data exists
        assert "Occurrence" in content or "verse" in content.lower()


class TestStrongsAPIRoutes:
    """Tests for Strong's API endpoints"""

    def test_api_strongs_entry(self, client):
        """Test API Strong's entry endpoint"""
        response = client.get("/api/strongs/G26")
        assert response.status_code == 200
        data = response.json()
        assert data["strongs"] == "G26"
        assert data["language"] == "Greek"

    def test_api_strongs_hebrew_entry(self, client):
        """Test API Hebrew Strong's entry"""
        response = client.get("/api/strongs/H430")
        assert response.status_code == 200
        data = response.json()
        assert data["strongs"] == "H430"
        assert data["language"] == "Hebrew"

    def test_api_strongs_invalid(self, client):
        """Test API invalid Strong's number"""
        response = client.get("/api/strongs/X9999")
        assert response.status_code == 404

    def test_api_strongs_search(self, client):
        """Test API Strong's search endpoint"""
        response = client.get("/api/strongs?q=love")
        assert response.status_code == 200
        data = response.json()
        assert "results" in data
        assert len(data["results"]) > 0


class TestStrongsEdgeCases:
    """Edge case tests for Strong's functionality"""

    def test_first_hebrew_entry(self, client):
        """Test first Hebrew entry (H1)"""
        response = client.get("/strongs/H1")
        assert response.status_code == 200

    def test_last_hebrew_entry(self, client):
        """Test last Hebrew entry (H8674)"""
        response = client.get("/strongs/H8674")
        assert response.status_code == 200

    def test_first_greek_entry(self, client):
        """Test first Greek entry (G1)"""
        response = client.get("/strongs/G1")
        assert response.status_code == 200

    def test_last_greek_entry(self, client):
        """Test last Greek entry (G5624)"""
        response = client.get("/strongs/G5624")
        assert response.status_code == 200

    def test_hebrew_boundary_plus_one(self, client):
        """Test beyond Hebrew range returns 404"""
        response = client.get("/strongs/H9999")
        assert response.status_code == 404

    def test_greek_boundary_plus_one(self, client):
        """Test beyond Greek range returns 404"""
        response = client.get("/strongs/G9999")
        assert response.status_code == 404

    def test_common_hebrew_words(self, client):
        """Test common Hebrew Strong's numbers"""
        common = ["H430", "H3068", "H1", "H7965", "H2617"]
        for num in common:
            response = client.get(f"/strongs/{num}")
            assert response.status_code == 200, f"Failed for {num}"

    def test_common_greek_words(self, client):
        """Test common Greek Strong's numbers"""
        common = ["G26", "G2316", "G3056", "G4102", "G5485"]
        for num in common:
            response = client.get(f"/strongs/{num}")
            assert response.status_code == 200, f"Failed for {num}"

    def test_strongs_with_derivation_links(self, client):
        """Test entry with derivation containing Strong's references"""
        # H1580 has derivation with references to other Strong's numbers
        response = client.get("/strongs/H1580")
        assert response.status_code == 200
        content = response.content.decode()
        # Should have linked Strong's references in derivation
        assert "/strongs/H" in content or "derivation" in content.lower()


class TestStrongsIntegration:
    """Integration tests for Strong's functionality"""

    def test_search_then_view_entry(self, client):
        """Test searching and then viewing an entry"""
        # Search for love
        search_response = client.get("/strongs?q=love")
        assert search_response.status_code == 200

        # View G26 (agape)
        entry_response = client.get("/strongs/G26")
        assert entry_response.status_code == 200
        content = entry_response.content.decode()
        assert "love" in content.lower()

    def test_browse_hebrew_then_entry(self, client):
        """Test browsing Hebrew index then viewing entry"""
        # Browse Hebrew
        index_response = client.get("/strongs/hebrew")
        assert index_response.status_code == 200

        # View first entry
        entry_response = client.get("/strongs/H1")
        assert entry_response.status_code == 200

    def test_navigate_between_entries(self, client):
        """Test navigation between Strong's entries"""
        response = client.get("/strongs/H100")
        assert response.status_code == 200
        content = response.content.decode()

        # Should have navigation to adjacent entries
        assert "H99" in content or "H101" in content

    def test_strongs_page_has_breadcrumbs(self, client):
        """Test Strong's pages have breadcrumb navigation"""
        response = client.get("/strongs/G26")
        assert response.status_code == 200
        content = response.content.decode()

        # Should have link back to Strong's index
        assert "/strongs" in content
