"""Tests for SuggestionEngine using real Friulian databases.

This test suite validates the Python spell checker implementation against
real Friulian SQLite databases from data/databases/ directory.
"""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

from furlan_spellchecker.database.manager import DatabaseManager
from furlan_spellchecker.phonetic.furlan_phonetic import FurlanPhoneticAlgorithm
from furlan_spellchecker.spellchecker.suggestion_engine import SuggestionEngine

sys.path.insert(0, str(Path(__file__).parent))
from database_utils import ensure_databases_extracted, get_database_paths, verify_database_files


@pytest.fixture(scope="session")
def real_databases():
    """Verify SQLite databases are available for testing."""
    # Ensure databases are extracted from ZIP files if needed
    ensure_databases_extracted()

    # Verify all databases are accessible
    if not verify_database_files():
        pytest.skip("Real SQLite databases not available or corrupted")

    return get_database_paths()


@pytest.fixture(scope="session")
def real_db_manager(real_databases, production_database_paths):
    """Create a DatabaseManager using real SQLite databases.

    Uses conftest.py's patch_database_io which redirects all database
    accesses to the production SQLite bundle in data/databases/.
    """
    from furlan_spellchecker.config.schemas import FurlanSpellCheckerConfig

    # Create config - the patch_database_io fixture handles routing to SQLite files
    config = FurlanSpellCheckerConfig()

    # Create DatabaseManager - it will use SQLite databases via the patched factory
    db_manager = DatabaseManager(config)

    return db_manager


@pytest.fixture(scope="session")
def real_suggestion_engine(real_db_manager):
    """Create a SuggestionEngine with real databases.

    No max_suggestions limit (like COF) - returns all suggestions.
    """
    phonetic_algo = FurlanPhoneticAlgorithm()
    return SuggestionEngine(db_manager=real_db_manager, phonetic=phonetic_algo)


class TestRealDatabaseIntegration:
    """Test suite using real Friulian databases."""

    def test_database_files_exist(self, real_databases):
        """Verify all required database files are available."""
        required_dbs = ["words", "frequencies", "errors", "elisions", "radix_tree"]

        for db_type in required_dbs:
            assert db_type in real_databases
            assert real_databases[db_type].exists()
            assert real_databases[db_type].is_file()

    def test_suggestion_engine_initialization(self, real_suggestion_engine):
        """Verify SuggestionEngine initializes properly with real databases."""
        assert real_suggestion_engine is not None
        assert real_suggestion_engine.db is not None
        assert real_suggestion_engine.phonetic is not None

    def test_furla_suggestions(self, real_suggestion_engine):
        """Test suggestions for 'furla' using real databases."""
        suggestions = real_suggestion_engine.suggest("furla")

        print(f"furla suggestions: {suggestions}")

        # With current Python databases, 'furla' may not have error corrections
        # This is different from COF behavior and represents current state
        assert isinstance(suggestions, list)

        # The Python engine with current databases doesn't suggest for 'furla'
        # This is a documented behavioral difference from COF

    def test_correct_word_suggestions(self, real_suggestion_engine):
        """Test suggestions for words that are already correct."""
        # Test a known correct Friulian word
        suggestions = real_suggestion_engine.suggest("furlan")

        print(f"furlan suggestions: {suggestions}")

        # Should return the word itself and potentially variants
        assert len(suggestions) >= 1
        assert "furlan" in [s.lower() for s in suggestions]

    def test_elision_handling(self, real_suggestion_engine):
        """Test elision expansion with real databases."""
        suggestions = real_suggestion_engine.suggest("l'aghe")

        suggestions_lower = [s.lower() for s in suggestions]
        print(f"l'aghe suggestions: {suggestions_lower}")

        # Should handle elisions (current behavior may vary)
        assert isinstance(suggestions, list)

        # Test specific elision expansion if implemented
        if suggestions:
            # May include expanded form depending on elision database content
            pass  # Flexible assertion for current implementation

    def test_case_preservation(self, real_suggestion_engine):
        """Test case preservation in suggestions."""
        # Test uppercase
        upper_suggestions = real_suggestion_engine.suggest("FURLA")
        print(f"FURLA suggestions: {upper_suggestions}")

        if upper_suggestions:
            # First suggestion should preserve case style
            assert upper_suggestions[0].isupper() or upper_suggestions[0].lower() in [
                "furla",
                "furlan",
            ]

        # Test title case
        title_suggestions = real_suggestion_engine.suggest("Furla")
        print(f"Furla suggestions: {title_suggestions}")

        if title_suggestions:
            # Should preserve title case style
            first_suggestion = title_suggestions[0]
            assert first_suggestion[0].isupper() and first_suggestion[1:].islower()

    def test_hyphen_handling(self, real_suggestion_engine):
        """Test hyphenated word handling."""
        suggestions = real_suggestion_engine.suggest("cjase-parol")

        # Should handle hyphenated words (may return empty if not implemented)
        assert isinstance(suggestions, list)

        print(f"cjase-parol suggestions: {suggestions}")

        # TODO: When hyphen handling is implemented, validate specific behavior

    def test_friulian_characters(self, real_suggestion_engine):
        """Test handling of special Friulian characters."""
        test_cases = [
            "gnôf",  # circumflex
            "çucarut",  # cedilla
            "scuele",  # normal chars
        ]

        for word in test_cases:
            suggestions = real_suggestion_engine.suggest(word)
            print(f"{word} suggestions: {suggestions}")

            # Should return some suggestions for each
            assert isinstance(suggestions, list)

    def test_nonsense_word_handling(self, real_suggestion_engine):
        """Test behavior with nonsense words."""
        nonsense_words = ["xyzqwerty", "blablabla", "qqqqq"]

        for word in nonsense_words:
            suggestions = real_suggestion_engine.suggest(word)
            print(f"{word} suggestions: {suggestions}")

            # Should handle gracefully (may return empty or phonetic matches)
            assert isinstance(suggestions, list)

    def test_single_character_input(self, real_suggestion_engine):
        """Test single character input."""
        single_chars = ["a", "e", "i", "o", "u"]

        for char in single_chars:
            suggestions = real_suggestion_engine.suggest(char)
            print(f"'{char}' suggestions: {suggestions}")

            # Should handle single characters
            assert isinstance(suggestions, list)


class TestCOFAlgorithmIntegration:
    """Test cases for COF algorithm implementation with specialized database classes.

    These tests verify that the new ElisionDatabase, ErrorDatabase, and FrequencyDatabase
    classes work correctly and that the COF algorithm prioritization is implemented properly.
    """

    def test_individual_database_access(self, real_db_manager):
        """Test that individual database classes work correctly."""
        # Test ElisionDatabase - check if a known word exists
        has_elision = real_db_manager.elision_db.has_elision("Urbignà")
        assert isinstance(has_elision, bool)  # Should return boolean

        # Test ErrorDatabase - check a known error pattern
        error_correction = real_db_manager.error_db.get_correction("un'")
        assert error_correction == "une"  # Known correction from errors.sqlite

        # Test FrequencyDatabase - check a known word
        frequency = real_db_manager.frequency_db.get_frequency("Lessi")
        assert frequency == 21  # Known frequency from frequencies.sqlite

        # Test that non-existent words return appropriate defaults
        non_existent_freq = real_db_manager.frequency_db.get_frequency("nonexistentword")
        assert non_existent_freq == 0

        non_existent_error = real_db_manager.error_db.get_correction("nonexistentword")
        assert non_existent_error is None

    def test_cof_priority_system(self, real_suggestion_engine):
        """Test that COF priority system works correctly."""
        # Test error corrections have high priority
        suggestions = real_suggestion_engine.suggest("un'")
        assert len(suggestions) >= 1
        assert suggestions[0] == "une"  # Error correction should be first

        # Test that error corrections beat phonetic matches
        # This tests the COF algorithm implementation where errors have priority 2
        # while phonetic suggestions have priority 5 (but lower effective priority)
        error_suggestions = real_suggestion_engine.suggest("un'")
        assert "une" in error_suggestions[:2]  # Should be in top 2

    def test_frequency_integration(self, real_suggestion_engine):
        """Test that frequency values are properly integrated into suggestions."""
        # Test with words that should have frequency data
        suggestions = real_suggestion_engine.suggest("furla")

        # Verify we get suggestions (frequency integration should work)
        assert isinstance(suggestions, list)

        # Test that the suggestion engine doesn't crash on frequency lookup
        # The specific behavior depends on available phonetic matches
        if suggestions:
            # Should contain reasonable Friulian words
            assert all(isinstance(sugg, str) for sugg in suggestions)

    def test_elision_database_integration(self, real_db_manager):
        """Test elision database integration for apostrophe handling."""
        # Test has_elision method
        test_words = ["Urbignà", "aceleratîf"]  # Known words from elisions.sqlite

        for word in test_words:
            result = real_db_manager.elision_db.has_elision(word)
            assert isinstance(result, bool)
            # These words actually exist in elisions.sqlite so should return True
            assert result is True

        # Test with a word that definitely doesn't exist
        non_existent = real_db_manager.elision_db.has_elision("nonexistentword123")
        assert non_existent is False

    def test_error_database_patterns(self, real_db_manager):
        """Test error database pattern matching."""
        # Test known error patterns from errors.sqlite
        known_errors = {
            "un'": "une",
            "'a": "a",  # Another known pattern
        }

        for error, correction in known_errors.items():
            result = real_db_manager.error_db.get_correction(error)
            assert result == correction


class TestCOFBehavioralParity:
    """Test cases comparing Python engine behavior to COF reference data.

    These tests validate specific behaviors observed in the original COF
    Perl implementation using the same underlying databases.
    """

    def test_real_database_samples(self, real_suggestion_engine):
        """Test against actual Python engine behaviors with real databases."""
        # Test cases based on observed Python engine behavior
        test_cases = {
            "furlan": "furlan",  # Correct word should return itself
            "cjase": "cjase",  # Another correct word
            "a": "a",  # Single letter
            "e": "e",  # Single letter
        }

        for input_word, expected_first in test_cases.items():
            suggestions = real_suggestion_engine.suggest(input_word)

            print(f"{input_word} -> {suggestions}")

            # Should return suggestions for these known words
            assert len(suggestions) >= 1
            assert expected_first.lower() in [s.lower() for s in suggestions]

    def test_elision_behavior(self, real_suggestion_engine):
        """Test elision handling with current Python engine."""
        elision_cases = ["l'aghe", "un'ore", "d'estât", "l'an"]

        for elided in elision_cases:
            suggestions = real_suggestion_engine.suggest(elided)

            print(f"{elided} -> {suggestions}")

            # Test that elisions are handled gracefully
            assert isinstance(suggestions, list)

            # Current implementation may or may not expand elisions
            # This documents the current behavior rather than enforcing COF compatibility

    def test_frequency_based_ranking(self, real_suggestion_engine):
        """Test that more frequent words appear first in suggestions."""
        # Test with a word that has multiple valid suggestions
        suggestions = real_suggestion_engine.suggest("furlan")

        print(f"furlan suggestions for frequency test: {suggestions}")

        # Should return suggestions (exact match case)
        assert isinstance(suggestions, list)

        # TODO: Add specific frequency validation when behavior is clarified

    @pytest.mark.parametrize(
        "case_style,input_word",
        [
            ("lower", "furla"),
            ("upper", "FURLA"),
            ("title", "Furla"),
            ("mixed", "FuRlAn"),
        ],
    )
    def test_case_style_preservation(self, real_suggestion_engine, case_style, input_word):
        """Test case style preservation across different inputs."""
        suggestions = real_suggestion_engine.suggest(input_word)

        print(f"{case_style} case test - {input_word} -> {suggestions}")

        if suggestions:
            first_suggestion = suggestions[0]

            if case_style == "upper":
                # Should be uppercase or reasonable fallback
                assert first_suggestion.isupper() or first_suggestion.lower() in ["furla", "furlan"]
            elif case_style == "title":
                # Should be title case
                assert first_suggestion[0].isupper() and first_suggestion[1:].islower()
            else:
                # Lower and mixed should produce reasonable output
                assert isinstance(first_suggestion, str) and len(first_suggestion) > 0
