"""Tests for report_generator module."""

from unittest.mock import MagicMock


from local_deep_research.error_handling.report_generator import (
    ErrorReportGenerator,
)
from local_deep_research.error_handling.error_reporter import ErrorCategory


class TestErrorReportGeneratorInit:
    """Tests for ErrorReportGenerator initialization."""

    def test_initializes_error_reporter(self, error_report_generator):
        """Should initialize with ErrorReporter."""
        assert hasattr(error_report_generator, "error_reporter")
        assert error_report_generator.error_reporter is not None

    def test_accepts_optional_llm(self):
        """Should accept optional LLM (kept for compatibility)."""
        mock_llm = MagicMock()
        generator = ErrorReportGenerator(llm=mock_llm)
        # LLM is not used, just accepted
        assert generator is not None


class TestGenerateErrorReport:
    """Tests for generate_error_report method."""

    def test_returns_markdown_string(self, error_report_generator):
        """Should return markdown string."""
        result = error_report_generator.generate_error_report(
            "Connection refused", "test query"
        )
        assert isinstance(result, str)
        assert "#" in result  # Has markdown headers

    def test_includes_error_type(self, error_report_generator):
        """Should include error type in report."""
        result = error_report_generator.generate_error_report(
            "Connection refused", "test query"
        )
        assert "Error Type:" in result

    def test_includes_what_happened(self, error_report_generator):
        """Should include what happened section."""
        result = error_report_generator.generate_error_report(
            "Connection refused", "test query"
        )
        assert "What happened:" in result

    def test_includes_help_links(self, error_report_generator):
        """Should include help links."""
        result = error_report_generator.generate_error_report(
            "Connection refused", "test query"
        )
        assert "Get Help" in result
        assert "Wiki" in result
        assert "Discord" in result
        assert "GitHub Issues" in result

    def test_includes_partial_results_when_available(
        self, error_report_generator, sample_partial_results
    ):
        """Should include partial results when available."""
        result = error_report_generator.generate_error_report(
            "Connection refused",
            "test query",
            partial_results=sample_partial_results,
        )
        assert "Partial Results" in result

    def test_handles_no_partial_results(self, error_report_generator):
        """Should handle no partial results gracefully."""
        result = error_report_generator.generate_error_report(
            "Connection refused", "test query", partial_results=None
        )
        assert isinstance(result, str)
        # Should not include partial results section
        assert "Partial Results" not in result

    def test_includes_research_id_when_provided(self, error_report_generator):
        """Should accept research_id parameter."""
        result = error_report_generator.generate_error_report(
            "Error", "query", research_id="test-uuid"
        )
        assert isinstance(result, str)

    def test_includes_search_iterations_when_provided(
        self, error_report_generator
    ):
        """Should accept search_iterations parameter."""
        result = error_report_generator.generate_error_report(
            "Error", "query", search_iterations=5
        )
        assert isinstance(result, str)

    def test_fallback_on_exception(self, error_report_generator):
        """Should return fallback report on exception."""
        # Mock error_reporter to raise exception
        error_report_generator.error_reporter.analyze_error = MagicMock(
            side_effect=Exception("test error")
        )
        result = error_report_generator.generate_error_report("Error", "query")
        assert "Research Failed" in result
        assert "Error report generation failed" in result


class TestFormatPartialResults:
    """Tests for _format_partial_results method."""

    def test_returns_empty_for_none(self, error_report_generator):
        """Should return empty string for None."""
        result = error_report_generator._format_partial_results(None)
        assert result == ""

    def test_returns_empty_for_empty_dict(self, error_report_generator):
        """Should return empty string for empty dict."""
        result = error_report_generator._format_partial_results({})
        assert result == ""

    def test_formats_current_knowledge(self, error_report_generator):
        """Should format current_knowledge section."""
        partial = {"current_knowledge": "This is knowledge content " * 10}
        result = error_report_generator._format_partial_results(partial)
        assert "Research Summary" in result

    def test_truncates_long_knowledge(self, error_report_generator):
        """Should truncate knowledge over 1000 chars."""
        long_content = "x" * 1500
        partial = {"current_knowledge": long_content}
        result = error_report_generator._format_partial_results(partial)
        assert "..." in result

    def test_skips_short_knowledge(self, error_report_generator):
        """Should skip knowledge under 50 chars."""
        partial = {"current_knowledge": "short"}
        result = error_report_generator._format_partial_results(partial)
        assert "Research Summary" not in result

    def test_formats_search_results(self, error_report_generator):
        """Should format search_results section."""
        partial = {
            "search_results": [
                {"title": "Result 1", "url": "https://example.com/1"},
                {"title": "Result 2", "url": "https://example.com/2"},
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        assert "Search Results Found" in result
        assert "Result 1" in result
        assert "https://example.com/1" in result

    def test_limits_search_results_to_five(self, error_report_generator):
        """Should show only top 5 search results."""
        partial = {
            "search_results": [
                {"title": f"Result {i}", "url": f"https://example.com/{i}"}
                for i in range(10)
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        # Results are 0-indexed, so 5 results means indices 0-4
        assert "Result 4" in result
        assert "Result 5" not in result

    def test_formats_findings(self, error_report_generator):
        """Should format findings section."""
        partial = {
            "findings": [
                {"phase": "Phase 1", "content": "Finding content 1"},
                {"phase": "Phase 2", "content": "Finding content 2"},
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        assert "Research Findings" in result
        assert "Phase 1" in result

    def test_skips_error_findings(self, error_report_generator):
        """Should skip findings that start with Error:."""
        partial = {
            "findings": [
                {"phase": "Phase 1", "content": "Error: Something went wrong"},
                {"phase": "Phase 2", "content": "Valid finding"},
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        assert "Valid finding" in result
        # Error finding should be skipped

    def test_limits_findings_to_three(self, error_report_generator):
        """Should show only top 3 findings."""
        partial = {
            "findings": [
                {"phase": f"Phase {i}", "content": f"Finding {i}"}
                for i in range(5)
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        assert "Phase 2" in result
        assert "Phase 4" not in result

    def test_truncates_long_finding_content(self, error_report_generator):
        """Should truncate finding content over 500 chars."""
        partial = {"findings": [{"phase": "Phase 1", "content": "x" * 600}]}
        result = error_report_generator._format_partial_results(partial)
        assert "..." in result

    def test_includes_note_when_results_exist(self, error_report_generator):
        """Should include note about successful collection."""
        partial = {
            "search_results": [
                {"title": "Result", "url": "https://example.com"}
            ]
        }
        result = error_report_generator._format_partial_results(partial)
        assert "successfully collected" in result


class TestGenerateQuickErrorSummary:
    """Tests for generate_quick_error_summary method."""

    def test_returns_dict(self, error_report_generator):
        """Should return dictionary."""
        result = error_report_generator.generate_quick_error_summary(
            "Connection refused"
        )
        assert isinstance(result, dict)

    def test_has_expected_keys(self, error_report_generator):
        """Should have expected keys."""
        result = error_report_generator.generate_quick_error_summary(
            "Connection refused"
        )
        expected_keys = ["title", "category", "severity", "recoverable"]
        for key in expected_keys:
            assert key in result

    def test_category_is_string_value(self, error_report_generator):
        """Should return category as string value."""
        result = error_report_generator.generate_quick_error_summary(
            "Connection refused"
        )
        assert isinstance(result["category"], str)

    def test_recoverable_is_boolean(self, error_report_generator):
        """Should return recoverable as boolean."""
        result = error_report_generator.generate_quick_error_summary(
            "Connection refused"
        )
        assert isinstance(result["recoverable"], bool)


class TestMakeErrorUserFriendly:
    """Tests for _make_error_user_friendly method."""

    def test_replaces_max_workers_error(self, error_report_generator):
        """Should replace max_workers error with friendly message."""
        result = error_report_generator._make_error_user_friendly(
            "max_workers must be greater than 0"
        )
        assert "LLM failed to generate" in result
        assert "Try this:" in result

    def test_replaces_ollama_eof_error(self, error_report_generator):
        """Should replace Ollama EOF error with friendly message."""
        result = error_report_generator._make_error_user_friendly(
            "POST predict encountered EOF"
        )
        assert "Lost connection to Ollama" in result

    def test_replaces_connection_refused(self, error_report_generator):
        """Should replace connection refused with friendly message."""
        result = error_report_generator._make_error_user_friendly(
            "Connection refused"
        )
        assert "Cannot connect to the LLM service" in result

    def test_replaces_search_too_long(self, error_report_generator):
        """Should replace search too long error."""
        result = error_report_generator._make_error_user_friendly(
            "The search is longer than 256 characters"
        )
        assert "search query is too long" in result

    def test_replaces_model_not_found(self, error_report_generator):
        """Should replace model not found error."""
        result = error_report_generator._make_error_user_friendly(
            "Model gemma not found in Ollama"
        )
        assert "model isn't available in Ollama" in result

    def test_replaces_api_key_error(self, error_report_generator):
        """Should replace API key errors."""
        result = error_report_generator._make_error_user_friendly(
            "No auth credentials found"
        )
        assert "API key is missing" in result

    def test_replaces_readonly_database(self, error_report_generator):
        """Should replace readonly database error."""
        result = error_report_generator._make_error_user_friendly(
            "Attempt to write readonly database"
        )
        assert "Permission issue" in result

    def test_replaces_docker_networking(self, error_report_generator):
        """Should replace Docker networking errors."""
        # Pattern requires "host.*localhost.*Docker" or similar
        result = error_report_generator._make_error_user_friendly(
            "Cannot connect to host at localhost via Docker"
        )
        # Should match docker pattern and suggest using host.docker.internal
        assert "Docker" in result or "host.docker.internal" in result

    def test_returns_original_for_unknown(self, error_report_generator):
        """Should return original message for unknown errors."""
        original = "Some random error that has no pattern"
        result = error_report_generator._make_error_user_friendly(original)
        assert result == original

    def test_includes_technical_error_in_replacement(
        self, error_report_generator
    ):
        """Should include technical error in replaced message."""
        result = error_report_generator._make_error_user_friendly(
            "max_workers must be greater than 0"
        )
        assert "Technical error:" in result
        assert "max_workers" in result

    def test_case_insensitive_matching(self, error_report_generator):
        """Should match patterns case-insensitively."""
        result = error_report_generator._make_error_user_friendly(
            "CONNECTION REFUSED"
        )
        assert "Cannot connect" in result


class TestGetTechnicalContext:
    """Tests for _get_technical_context method."""

    def test_returns_empty_for_no_context(self, error_report_generator):
        """Should return empty string for no context."""
        error_analysis = {"category": ErrorCategory.CONNECTION_ERROR}
        result = error_report_generator._get_technical_context(
            error_analysis, None
        )
        # May still have category-based context
        assert isinstance(result, str)

    def test_includes_timing_info(self, error_report_generator):
        """Should include timing info when available."""
        error_analysis = {"category": ErrorCategory.CONNECTION_ERROR}
        partial = {
            "start_time": "2024-01-01T10:00:00",
            "last_activity": "2024-01-01T10:05:00",
        }
        result = error_report_generator._get_technical_context(
            error_analysis, partial
        )
        assert "Start Time:" in result

    def test_includes_model_info(self, error_report_generator):
        """Should include model info when available."""
        error_analysis = {"category": ErrorCategory.MODEL_ERROR}
        partial = {
            "model_config": {"model_name": "gpt-4", "provider": "openai"}
        }
        result = error_report_generator._get_technical_context(
            error_analysis, partial
        )
        assert "Model:" in result

    def test_includes_search_info(self, error_report_generator):
        """Should include search info when available."""
        error_analysis = {"category": ErrorCategory.SEARCH_ERROR}
        partial = {"search_config": {"engine": "duckduckgo", "max_results": 10}}
        result = error_report_generator._get_technical_context(
            error_analysis, partial
        )
        assert "Search Engine:" in result

    def test_includes_error_codes(self, error_report_generator):
        """Should include error codes when available."""
        error_analysis = {"category": ErrorCategory.CONNECTION_ERROR}
        partial = {"status_code": 503, "error_code": "SERVICE_UNAVAILABLE"}
        result = error_report_generator._get_technical_context(
            error_analysis, partial
        )
        assert "Status Code:" in result
        assert "Error Code:" in result

    def test_adds_category_specific_context(self, error_report_generator):
        """Should add category-specific context."""
        error_analysis = {"category": ErrorCategory.CONNECTION_ERROR}
        result = error_report_generator._get_technical_context(
            error_analysis, {}
        )
        assert "Network Error:" in result

        error_analysis = {"category": ErrorCategory.MODEL_ERROR}
        result = error_report_generator._get_technical_context(
            error_analysis, {}
        )
        assert "Model Error:" in result
