"""
Tests for error_handling/error_reporter.py - Error Categorization

Tests cover:
- Error message pattern matching
- Category assignment for different error types
- Edge cases in pattern matching
- User-friendly error information

These tests ensure users get helpful error messages and guidance.
"""

import pytest


class TestErrorCategorization:
    """Tests for error categorization logic."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_connection_error_detected(self, reporter):
        """'Connection refused' -> CONNECTION_ERROR."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Test various connection error patterns
        test_cases = [
            "Connection refused",
            "POST predict EOF error",
            "Connection failed",
            "timeout waiting for response",
            "HTTP error 500",
            "network error occurred",
            "[Errno 111] Connection refused",
            "host.docker.internal not reachable",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.CONNECTION_ERROR, (
                f"Failed for: {error_msg}"
            )

    def test_model_error_detected(self, reporter):
        """'Model not found' -> MODEL_ERROR."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        test_cases = [
            "Model xyz not found",
            "Invalid model specified",
            "Ollama is not available",
            "API key is invalid",
            "Authentication error",
            "max_workers must be greater than 0",
            "TypeError Context Size",
            "No auth credentials found",
            "401 - API key",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.MODEL_ERROR, (
                f"Failed for: {error_msg}"
            )

    def test_rate_limit_error_detected(self, reporter):
        """'rate limit' -> RATE_LIMIT_ERROR."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        test_cases = [
            "429 resource exhausted",
            "429 too many requests",
            "rate limit exceeded",
            "rate_limit hit",
            "ratelimit reached",
            "quota exceeded",
            "resource exhausted - quota",
            "LLM rate limit reached",
            "API rate limit",
            "maximum requests per minute",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.RATE_LIMIT_ERROR, (
                f"Failed for: {error_msg}"
            )

    def test_timeout_error_detected(self, reporter):
        """'timeout' -> CONNECTION_ERROR (timeout is connection-related)."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Note: timeout is in CONNECTION_ERROR patterns
        # The pattern matches "timeout" exactly (case-insensitive)
        test_cases = [
            "timeout",
            "Connection timeout",
            "The request timeout occurred",  # Contains "timeout"
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            # Timeout is categorized as CONNECTION_ERROR
            assert category == ErrorCategory.CONNECTION_ERROR, (
                f"Failed for: {error_msg}"
            )

    def test_overlapping_patterns_priority(self, reporter):
        """First matching pattern wins."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Test message that could match multiple patterns
        # "Connection timeout" has both "Connection" and "timeout"
        category = reporter.categorize_error("Connection timeout")

        # Should match CONNECTION_ERROR first
        assert category == ErrorCategory.CONNECTION_ERROR

    def test_partial_match_rejected(self, reporter):
        """'settimeout' doesn't match 'timeout' pattern."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # The regex pattern is just "timeout", which will match "settimeout"
        # This tests the actual behavior
        category = reporter.categorize_error("settimeout error occurred")

        # Note: This WILL match because regex doesn't have word boundaries
        # The pattern "timeout" is contained in "settimeout"
        # This test documents the current behavior
        assert category == ErrorCategory.CONNECTION_ERROR

    def test_case_insensitive_matching(self, reporter):
        """'TIMEOUT' matches timeout pattern."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        test_cases = [
            ("TIMEOUT", ErrorCategory.CONNECTION_ERROR),
            ("RATE LIMIT", ErrorCategory.RATE_LIMIT_ERROR),
            ("MODEL NOT FOUND", ErrorCategory.MODEL_ERROR),
            ("CONNECTION REFUSED", ErrorCategory.CONNECTION_ERROR),
        ]

        for error_msg, expected in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == expected, f"Failed for: {error_msg}"

    def test_multiline_error_message(self, reporter):
        """Multi-line errors parsed correctly."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        multiline_error = """
        Error occurred:
        Connection refused
        at line 123
        in file xyz.py
        """

        category = reporter.categorize_error(multiline_error)
        assert category == ErrorCategory.CONNECTION_ERROR

    def test_empty_error_returns_unknown(self, reporter):
        """Empty string -> UNKNOWN_ERROR."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        category = reporter.categorize_error("")
        assert category == ErrorCategory.UNKNOWN_ERROR

        category = reporter.categorize_error("   ")
        assert category == ErrorCategory.UNKNOWN_ERROR

    def test_very_long_error_performance(self, reporter):
        """10KB error message doesn't hang."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Create a very long error message (10KB)
        long_error = "x" * 10000 + " Connection refused " + "y" * 10000

        import time

        start = time.time()
        category = reporter.categorize_error(long_error)
        elapsed = time.time() - start

        # Should complete within reasonable time (< 1 second)
        assert elapsed < 1.0
        assert category == ErrorCategory.CONNECTION_ERROR


class TestUserFriendlyTitles:
    """Tests for user-friendly error titles."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_all_categories_have_titles(self, reporter):
        """All error categories have user-friendly titles."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        for category in ErrorCategory:
            title = reporter.get_user_friendly_title(category)
            assert title is not None
            assert len(title) > 0

    def test_title_content(self, reporter):
        """Titles are meaningful."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        expected_titles = {
            ErrorCategory.CONNECTION_ERROR: "Connection Issue",
            ErrorCategory.MODEL_ERROR: "LLM Service Error",
            ErrorCategory.SEARCH_ERROR: "Search Service Error",
            ErrorCategory.RATE_LIMIT_ERROR: "API Rate Limit Exceeded",
            ErrorCategory.UNKNOWN_ERROR: "Unexpected Error",
        }

        for category, expected in expected_titles.items():
            actual = reporter.get_user_friendly_title(category)
            assert actual == expected


class TestSuggestedActions:
    """Tests for suggested action lists."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_all_categories_have_suggestions(self, reporter):
        """All categories have suggested actions."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        for category in ErrorCategory:
            suggestions = reporter.get_suggested_actions(category)
            assert suggestions is not None
            assert isinstance(suggestions, list)
            assert len(suggestions) > 0

    def test_suggestions_are_actionable(self, reporter):
        """Suggestions contain actionable text."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        suggestions = reporter.get_suggested_actions(
            ErrorCategory.CONNECTION_ERROR
        )

        # Should have multiple suggestions
        assert len(suggestions) >= 2

        # Each should be a non-empty string
        for suggestion in suggestions:
            assert isinstance(suggestion, str)
            assert len(suggestion) > 10  # Meaningful text


class TestErrorAnalysis:
    """Tests for comprehensive error analysis."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_analyze_error_returns_complete_structure(self, reporter):
        """analyze_error returns all expected keys."""
        analysis = reporter.analyze_error("Connection refused")

        assert "category" in analysis
        assert "title" in analysis
        assert "original_error" in analysis
        assert "suggestions" in analysis
        assert "severity" in analysis
        assert "recoverable" in analysis

    def test_analyze_error_with_context(self, reporter):
        """Context information is included."""
        context = {
            "findings": [{"content": "some data"}],
            "current_knowledge": "existing info",
        }

        analysis = reporter.analyze_error("Connection refused", context=context)

        assert "context" in analysis
        assert "has_partial_results" in analysis
        assert analysis["has_partial_results"] is True

    def test_severity_levels(self, reporter):
        """Severity levels are appropriate."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        severity_expectations = {
            ErrorCategory.CONNECTION_ERROR: "high",
            ErrorCategory.MODEL_ERROR: "high",
            ErrorCategory.SEARCH_ERROR: "medium",
            ErrorCategory.SYNTHESIS_ERROR: "low",
            ErrorCategory.FILE_ERROR: "medium",
            ErrorCategory.RATE_LIMIT_ERROR: "medium",
            ErrorCategory.UNKNOWN_ERROR: "high",
        }

        for category, expected_severity in severity_expectations.items():
            actual = reporter._determine_severity(category)
            assert actual == expected_severity, f"Failed for {category}"

    def test_recoverability(self, reporter):
        """Recoverability is correctly determined."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Most errors should be recoverable
        recoverable_categories = [
            ErrorCategory.CONNECTION_ERROR,
            ErrorCategory.MODEL_ERROR,
            ErrorCategory.SEARCH_ERROR,
            ErrorCategory.SYNTHESIS_ERROR,
            ErrorCategory.FILE_ERROR,
            ErrorCategory.RATE_LIMIT_ERROR,
        ]

        for category in recoverable_categories:
            assert reporter._is_recoverable(category) is True

        # Unknown errors are not recoverable
        assert reporter._is_recoverable(ErrorCategory.UNKNOWN_ERROR) is False


class TestSearchErrorPatterns:
    """Tests for search-related error patterns."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_search_error_patterns(self, reporter):
        """Search error patterns are detected."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        test_cases = [
            "Search failed",
            "No search results found",
            "Search engine error",
            "The search is longer than 256 characters",
            "Failed to create search engine",
            "could not be found",
            "GitHub API error",
            "database is locked",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.SEARCH_ERROR, (
                f"Failed for: {error_msg}"
            )


class TestSynthesisErrorPatterns:
    """Tests for synthesis-related error patterns."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_synthesis_error_patterns(self, reporter):
        """Synthesis error patterns are detected."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Note: "Synthesis timeout" would match CONNECTION_ERROR due to "timeout"
        # Pattern matching is priority-based
        test_cases = [
            "Error during synthesis",
            "Failed to generate report",
            "detailed report stuck",
            "report taking too long",
            "progress at 100 stuck",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.SYNTHESIS_ERROR, (
                f"Failed for: {error_msg}"
            )


class TestFileErrorPatterns:
    """Tests for file-related error patterns."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_file_error_patterns(self, reporter):
        """File error patterns are detected."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorCategory,
        )

        # Note: "HTTP error 404" would match CONNECTION_ERROR first
        test_cases = [
            "Permission denied",
            "File xyz not found",
            "Cannot write to file",
            "Disk is full",
            "No module named local_deep_research",
            "Attempt to write readonly database",
        ]

        for error_msg in test_cases:
            category = reporter.categorize_error(error_msg)
            assert category == ErrorCategory.FILE_ERROR, (
                f"Failed for: {error_msg}"
            )


class TestServiceNameExtraction:
    """Tests for service name extraction from errors."""

    @pytest.fixture
    def reporter(self):
        """Create an ErrorReporter instance."""
        from local_deep_research.error_handling.error_reporter import (
            ErrorReporter,
        )

        return ErrorReporter()

    def test_extract_service_names(self, reporter):
        """Service names are extracted from error messages."""
        test_cases = [
            ("OpenAI API error", "Openai"),
            ("Anthropic rate limit", "Anthropic"),
            ("Google API error", "Google"),
            ("Ollama connection failed", "Ollama"),
            ("SearXNG timeout", "Searxng"),
            ("Tavily search failed", "Tavily"),
            ("Brave search error", "Brave"),
            ("Unknown service error", "API Service"),
        ]

        for error_msg, expected_service in test_cases:
            actual = reporter._extract_service_name(error_msg)
            assert actual == expected_service, f"Failed for: {error_msg}"
