"""High-value tests for error_handling and text_processing modules.

Covers gaps not addressed by existing tests:
- ErrorReporter._extract_service_name for all known services
- ErrorReporter severity and recoverability for all categories
- ErrorReportGenerator._make_error_user_friendly pattern matching
- ErrorReportGenerator.generate_quick_error_summary
- ErrorReportGenerator._get_technical_context
- text_cleaner.remove_surrogates fallback path
"""

from unittest.mock import MagicMock

from local_deep_research.error_handling.error_reporter import (
    ErrorReporter,
    ErrorCategory,
)
from local_deep_research.error_handling.report_generator import (
    ErrorReportGenerator,
)
from local_deep_research.text_processing.text_cleaner import remove_surrogates


class TestExtractServiceName:
    """Test ErrorReporter._extract_service_name."""

    def test_detects_openai(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("OpenAI API error 429") == "Openai"
        )

    def test_detects_anthropic(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("Anthropic rate limit")
            == "Anthropic"
        )

    def test_detects_ollama(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("Ollama connection refused")
            == "Ollama"
        )

    def test_detects_google(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("Google API quota exceeded")
            == "Google"
        )

    def test_detects_tavily(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("Tavily search failed") == "Tavily"
        )

    def test_detects_brave(self):
        reporter = ErrorReporter()
        assert reporter._extract_service_name("Brave API error") == "Brave"

    def test_returns_default_for_unknown(self):
        reporter = ErrorReporter()
        assert reporter._extract_service_name("Unknown error") == "API Service"

    def test_case_insensitive_detection(self):
        reporter = ErrorReporter()
        assert (
            reporter._extract_service_name("SEARXNG configuration error")
            == "Searxng"
        )


class TestSeverityAndRecoverability:
    """Test severity and recoverability for all error categories."""

    def test_all_categories_have_severity(self):
        """Every ErrorCategory has a defined severity."""
        reporter = ErrorReporter()
        for cat in ErrorCategory:
            severity = reporter._determine_severity(cat)
            assert severity in ("low", "medium", "high")

    def test_all_categories_have_recoverability(self):
        """Every ErrorCategory has a recoverability value."""
        reporter = ErrorReporter()
        for cat in ErrorCategory:
            recoverable = reporter._is_recoverable(cat)
            assert isinstance(recoverable, bool)

    def test_unknown_error_is_not_recoverable(self):
        reporter = ErrorReporter()
        assert reporter._is_recoverable(ErrorCategory.UNKNOWN_ERROR) is False

    def test_connection_error_is_recoverable(self):
        reporter = ErrorReporter()
        assert reporter._is_recoverable(ErrorCategory.CONNECTION_ERROR) is True

    def test_rate_limit_is_medium_severity(self):
        reporter = ErrorReporter()
        assert (
            reporter._determine_severity(ErrorCategory.RATE_LIMIT_ERROR)
            == "medium"
        )


class TestUserFriendlyErrorMessages:
    """Test ErrorReportGenerator._make_error_user_friendly."""

    def test_max_workers_replacement(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly(
            "max_workers must be greater than 0"
        )
        assert "LLM failed to generate search questions" in result
        assert "Technical error:" in result

    def test_connection_refused_replacement(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly("Connection refused [Errno 111]")
        assert "Cannot connect to the LLM service" in result

    def test_github_search_too_long(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly(
            "The search is longer than 256 characters"
        )
        assert "too long for GitHub" in result

    def test_model_not_found_ollama(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly("Model xyz not found in Ollama")
        assert "isn't available in Ollama" in result

    def test_no_auth_credentials(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly(
            "No auth credentials found for API"
        )
        assert "API key is missing" in result

    def test_readonly_database(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly(
            "Attempt to write readonly database"
        )
        assert "Permission issue" in result

    def test_unmatched_error_returns_original(self):
        gen = ErrorReportGenerator()
        original = "Some completely unique error that doesn't match any pattern"
        result = gen._make_error_user_friendly(original)
        assert result == original

    def test_no_search_results_replacement(self):
        gen = ErrorReportGenerator()
        result = gen._make_error_user_friendly(
            "No search results found for query"
        )
        assert "No search results were found" in result


class TestGenerateQuickErrorSummary:
    """Test ErrorReportGenerator.generate_quick_error_summary."""

    def test_returns_dict_with_expected_keys(self):
        gen = ErrorReportGenerator()
        result = gen.generate_quick_error_summary("Connection refused")
        expected_keys = {"title", "category", "severity", "recoverable"}
        assert set(result.keys()) == expected_keys

    def test_connection_error_summary(self):
        gen = ErrorReportGenerator()
        result = gen.generate_quick_error_summary(
            "Connection refused [Errno 111]"
        )
        assert result["category"] == "connection_error"
        assert result["title"] == "Connection Issue"
        assert result["severity"] == "high"
        assert result["recoverable"] is True

    def test_unknown_error_summary(self):
        gen = ErrorReportGenerator()
        result = gen.generate_quick_error_summary(
            "Something totally unknown happened"
        )
        assert result["category"] == "unknown_error"
        assert result["recoverable"] is False


class TestGetTechnicalContext:
    """Test ErrorReportGenerator._get_technical_context."""

    def test_empty_when_no_partial_results(self):
        gen = ErrorReportGenerator()
        result = gen._get_technical_context({"category": None}, None)
        assert result == ""

    def test_includes_start_time(self):
        gen = ErrorReportGenerator()
        partial = {"start_time": "2024-01-01T00:00:00"}
        result = gen._get_technical_context({}, partial)
        assert "Start Time" in result

    def test_includes_model_config(self):
        gen = ErrorReportGenerator()
        partial = {
            "model_config": {"model_name": "gpt-4", "provider": "openai"}
        }
        result = gen._get_technical_context({}, partial)
        assert "gpt-4" in result
        assert "openai" in result

    def test_includes_search_config(self):
        gen = ErrorReportGenerator()
        partial = {"search_config": {"engine": "duckduckgo", "max_results": 10}}
        result = gen._get_technical_context({}, partial)
        assert "duckduckgo" in result

    def test_connection_category_context(self):
        gen = ErrorReportGenerator()
        mock_cat = MagicMock()
        mock_cat.value = "connection_error"
        result = gen._get_technical_context({"category": mock_cat}, {})
        assert "Network Error" in result


class TestGenerateErrorReportIntegration:
    """Integration tests for generate_error_report."""

    def test_report_contains_header(self):
        gen = ErrorReportGenerator()
        report = gen.generate_error_report("test error", "test query")
        assert "# ⚠️ Research Failed" in report

    def test_report_contains_help_links(self):
        gen = ErrorReportGenerator()
        report = gen.generate_error_report("test error", "test query")
        assert "Discord" in report
        assert "GitHub Issues" in report

    def test_report_with_partial_results_has_details(self):
        gen = ErrorReportGenerator()
        partial = {
            "current_knowledge": "A" * 100,  # > 50 chars
            "findings": [{"content": "Finding 1", "phase": "Phase 1"}],
        }
        report = gen.generate_error_report(
            "test error", "test query", partial_results=partial
        )
        assert "Partial Results" in report


class TestRemoveSurrogatesFallbackPath:
    """Test text_cleaner.remove_surrogates edge cases."""

    def test_falsy_zero_returns_zero(self):
        """remove_surrogates returns falsy input as-is."""
        result = remove_surrogates(0)
        assert result == 0

    def test_falsy_empty_string_returns_empty(self):
        result = remove_surrogates("")
        assert result == ""

    def test_falsy_none_returns_none(self):
        result = remove_surrogates(None)
        assert result is None

    def test_normal_ascii_unchanged(self):
        result = remove_surrogates("Hello, World!")
        assert result == "Hello, World!"

    def test_emoji_preserved(self):
        """Valid emoji characters are preserved."""
        result = remove_surrogates("Test 🎉 emoji 🚀")
        assert "🎉" in result
        assert "🚀" in result

    def test_mixed_cjk_preserved(self):
        """Chinese/Japanese/Korean characters preserved."""
        result = remove_surrogates("Hello 你好 こんにちは")
        assert "你好" in result
        assert "こんにちは" in result
