"""
Tests for Knowledge Generator classes.

Phase 31: Knowledge & Questions - Tests for knowledge extraction and generation.
Tests StandardKnowledge and BaseKnowledgeGenerator functionality.
"""

from unittest.mock import MagicMock

from local_deep_research.advanced_search_system.knowledge.base_knowledge import (
    BaseKnowledgeGenerator,
)
from local_deep_research.advanced_search_system.knowledge.standard_knowledge import (
    StandardKnowledge,
)


class TestBaseKnowledgeGenerator:
    """Tests for BaseKnowledgeGenerator abstract class."""

    def test_validate_knowledge_valid_string(self):
        """Test validation passes for valid knowledge string."""
        mock_model = MagicMock()

        # Create concrete implementation for testing
        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_knowledge("Valid knowledge text") is True

    def test_validate_knowledge_empty_string(self):
        """Test validation fails for empty knowledge string."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_knowledge("") is False

    def test_validate_knowledge_none(self):
        """Test validation fails for None knowledge."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_knowledge(None) is False

    def test_validate_knowledge_non_string(self):
        """Test validation fails for non-string knowledge."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_knowledge(123) is False

    def test_validate_links_valid_list(self):
        """Test validation passes for valid links list."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert (
            generator._validate_links(["http://example.com", "http://test.com"])
            is True
        )

    def test_validate_links_empty_list(self):
        """Test validation passes for empty links list."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_links([]) is True

    def test_validate_links_non_list(self):
        """Test validation fails for non-list links."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_links("not a list") is False

    def test_validate_links_non_string_elements(self):
        """Test validation fails for links list with non-string elements."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator._validate_links(["http://example.com", 123]) is False

    def test_extract_key_points(self):
        """Test key points extraction splits on newlines."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        result = generator._extract_key_points("Point 1\nPoint 2\nPoint 3")
        assert len(result) == 3
        assert result[0] == "Point 1"
        assert result[2] == "Point 3"

    def test_initialization_with_model(self):
        """Test generator initializes with provided model."""
        mock_model = MagicMock()

        class ConcreteKnowledge(BaseKnowledgeGenerator):
            def generate(self, query, context):
                return ""

            def generate_knowledge(
                self, query, context="", current_knowledge="", questions=None
            ):
                return ""

            def generate_sub_knowledge(self, sub_query, context=""):
                return ""

            def compress_knowledge(
                self, current_knowledge, query, section_links, **kwargs
            ):
                return ""

            def format_citations(self, links):
                return ""

        generator = ConcreteKnowledge(mock_model)
        assert generator.model is mock_model


class TestStandardKnowledge:
    """Tests for StandardKnowledge implementation."""

    def test_initialization(self):
        """Test StandardKnowledge initializes correctly."""
        mock_model = MagicMock()
        generator = StandardKnowledge(mock_model)
        assert generator.model is mock_model

    def test_generate_knowledge_basic_query(self):
        """Test knowledge generation with basic query."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Generated knowledge content"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_knowledge("What is AI?")

        assert result == "Generated knowledge content"
        mock_model.invoke.assert_called_once()

    def test_generate_knowledge_with_context(self):
        """Test knowledge generation with context."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Contextual knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_knowledge(
            "What is AI?", context="Focus on machine learning"
        )

        assert result == "Contextual knowledge"
        call_args = mock_model.invoke.call_args[0][0]
        assert "Focus on machine learning" in call_args

    def test_generate_knowledge_with_current_knowledge(self):
        """Test knowledge generation with existing knowledge."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Extended knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_knowledge(
            "What is AI?", current_knowledge="AI is artificial intelligence"
        )

        assert result == "Extended knowledge"
        call_args = mock_model.invoke.call_args[0][0]
        assert "AI is artificial intelligence" in call_args

    def test_generate_knowledge_with_questions(self):
        """Test knowledge generation with questions to address."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Knowledge addressing questions"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_knowledge(
            "What is AI?",
            questions=["How does it work?", "What are the applications?"],
        )

        assert result == "Knowledge addressing questions"
        call_args = mock_model.invoke.call_args[0][0]
        assert "How does it work?" in call_args
        assert "Addresses each question" in call_args

    def test_generate_sub_knowledge_success(self):
        """Test sub-knowledge generation succeeds."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Sub-knowledge content"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_sub_knowledge("What is neural network?")

        assert result == "Sub-knowledge content"

    def test_generate_sub_knowledge_with_context(self):
        """Test sub-knowledge generation with context."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Contextual sub-knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_sub_knowledge(
            "What is neural network?", context="In context of deep learning"
        )

        assert result == "Contextual sub-knowledge"
        call_args = mock_model.invoke.call_args[0][0]
        assert "In context of deep learning" in call_args

    def test_generate_sub_knowledge_error_handling(self):
        """Test sub-knowledge generation handles errors."""
        mock_model = MagicMock()
        mock_model.invoke.side_effect = Exception("Model error")

        generator = StandardKnowledge(mock_model)
        result = generator.generate_sub_knowledge("What is neural network?")

        assert result == ""

    def test_generate_method_delegates_to_generate_knowledge(self):
        """Test generate() delegates to generate_knowledge()."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Generated content"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate("Test query", "Test context")

        assert result == "Generated content"

    def test_compress_knowledge_success(self):
        """Test knowledge compression succeeds."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Compressed knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.compress_knowledge(
            "Very long knowledge text " * 100,
            "Original query",
            ["http://source1.com"],
        )

        assert result == "Compressed knowledge"
        call_args = mock_model.invoke.call_args[0][0]
        assert "Original query" in call_args

    def test_compress_knowledge_error_returns_original(self):
        """Test knowledge compression returns original on error."""
        mock_model = MagicMock()
        mock_model.invoke.side_effect = Exception("Compression error")

        generator = StandardKnowledge(mock_model)
        original = "Original knowledge text"
        result = generator.compress_knowledge(original, "query", [])

        assert result == original

    def test_format_citations_single_link(self):
        """Test formatting a single citation."""
        mock_model = MagicMock()
        generator = StandardKnowledge(mock_model)

        result = generator.format_citations(["http://example.com"])

        assert result == "[1] http://example.com"

    def test_format_citations_multiple_links(self):
        """Test formatting multiple citations."""
        mock_model = MagicMock()
        generator = StandardKnowledge(mock_model)

        result = generator.format_citations(
            [
                "http://example1.com",
                "http://example2.com",
                "http://example3.com",
            ]
        )

        assert "[1] http://example1.com" in result
        assert "[2] http://example2.com" in result
        assert "[3] http://example3.com" in result

    def test_format_citations_empty_list(self):
        """Test formatting returns empty string for empty list."""
        mock_model = MagicMock()
        generator = StandardKnowledge(mock_model)

        result = generator.format_citations([])

        assert result == ""

    def test_generate_knowledge_includes_timestamp(self):
        """Test knowledge generation includes current timestamp."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Time-aware knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        generator.generate_knowledge("Test query")

        call_args = mock_model.invoke.call_args[0][0]
        assert "Current Time:" in call_args


class TestKnowledgeGeneratorIntegration:
    """Integration tests for knowledge generator components."""

    def test_full_knowledge_workflow(self):
        """Test complete knowledge generation workflow."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Comprehensive knowledge about AI"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)

        # Generate initial knowledge
        initial = generator.generate_knowledge("What is AI?")
        assert initial == "Comprehensive knowledge about AI"

        # Generate sub-knowledge
        mock_response.content = "Sub-topic knowledge"
        sub = generator.generate_sub_knowledge("Neural networks")
        assert sub == "Sub-topic knowledge"

        # Compress accumulated knowledge
        mock_response.content = "Compressed result"
        compressed = generator.compress_knowledge(
            initial + sub, "What is AI?", ["http://source.com"]
        )
        assert compressed == "Compressed result"

        # Format citations
        citations = generator.format_citations(["http://source.com"])
        assert "[1]" in citations

    def test_knowledge_with_all_parameters(self):
        """Test knowledge generation with all parameters."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Complete knowledge"
        mock_model.invoke.return_value = mock_response

        generator = StandardKnowledge(mock_model)
        result = generator.generate_knowledge(
            query="Main query",
            context="Research context",
            current_knowledge="Existing knowledge base",
            questions=["Q1?", "Q2?", "Q3?"],
        )

        assert result == "Complete knowledge"
        call_args = mock_model.invoke.call_args[0][0]
        assert "Main query" in call_args
        assert "Research context" in call_args
        assert "Existing knowledge base" in call_args
        assert "Q1?" in call_args

    def test_error_recovery_workflow(self):
        """Test workflow recovers from errors gracefully."""
        mock_model = MagicMock()

        generator = StandardKnowledge(mock_model)

        # Simulate error in sub-knowledge generation
        mock_model.invoke.side_effect = Exception("API error")
        sub_result = generator.generate_sub_knowledge("Test")
        assert sub_result == ""

        # Simulate error in compression
        original = "Original knowledge"
        compress_result = generator.compress_knowledge(original, "query", [])
        assert compress_result == original
