"""
Tests for advanced_search_system/findings/repository.py

Tests cover:
- format_links function
- FindingsRepository initialization
- add_finding method
- get_findings method
- clear_findings method
- add_documents method
- set_questions_by_iteration method
- format_findings_to_text method
- synthesize_findings method
"""

from unittest.mock import Mock, patch


class TestFindingsRepositoryInit:
    """Tests for FindingsRepository initialization."""

    def test_initializes_with_model(self):
        """Test initialization with model."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        repo = FindingsRepository(mock_model)

        assert repo.model is mock_model
        assert repo.findings == {}
        assert repo.documents == []
        assert repo.questions_by_iteration == {}

    def test_inherits_from_base_findings(self):
        """Test inheritance from BaseFindingsRepository."""
        from local_deep_research.advanced_search_system.findings.base_findings import (
            BaseFindingsRepository,
        )
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        assert issubclass(FindingsRepository, BaseFindingsRepository)


class TestSetQuestionsByIteration:
    """Tests for set_questions_by_iteration method."""

    def test_makes_a_shallow_copy(self):
        """Test that a shallow copy is made (top-level dict is copied)."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        questions = {1: ["Q1"]}

        repo.set_questions_by_iteration(questions)
        # Adding a new key to original dict should not affect repo
        questions[2] = ["Q3"]

        # New key should not be in repo (shallow copy of dict)
        assert 2 not in repo.questions_by_iteration


class TestFormatFindingsToText:
    """Tests for format_findings_to_text method."""

    @patch(
        "local_deep_research.advanced_search_system.findings.repository.format_findings"
    )
    def test_formats_findings(self, mock_format):
        """Test formatting findings to text."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_format.return_value = "Formatted report"

        repo = FindingsRepository(Mock())
        findings_list = [{"phase": "Research", "content": "Finding"}]

        result = repo.format_findings_to_text(
            findings_list, "Synthesized content"
        )

        assert result == "Formatted report"
        mock_format.assert_called_once()

    @patch(
        "local_deep_research.advanced_search_system.findings.repository.format_findings"
    )
    def test_returns_fallback_on_error(self, mock_format):
        """Test fallback when formatting fails."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_format.side_effect = Exception("Format error")

        repo = FindingsRepository(Mock())

        result = repo.format_findings_to_text([], "Content")

        assert "Error during final formatting" in result
        assert "Content" in result


class TestSynthesizeFindings:
    """Tests for synthesize_findings method."""

    def test_synthesizes_with_model(self):
        """Test synthesizing findings with model."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Synthesized answer")

        repo = FindingsRepository(mock_model)
        findings = [{"content": "Finding 1"}, {"content": "Finding 2"}]

        result = repo.synthesize_findings(
            query="Test query", sub_queries=["Sub 1"], findings=findings
        )

        assert result == "Synthesized answer"

    def test_synthesizes_string_response(self):
        """synthesize_findings handles a raw string model return (no .content)."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = "Raw string answer [1]."

        repo = FindingsRepository(mock_model)
        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=["Finding 1"]
        )

        assert result == "Raw string answer [1]."

    def test_synthesizes_string_findings(self):
        """Test synthesizing string findings."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Synthesized")

        repo = FindingsRepository(mock_model)
        findings = ["Finding 1", "Finding 2"]

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=findings
        )

        assert result == "Synthesized"

    def test_uses_accumulated_knowledge_when_provided(self):
        """Test using provided accumulated knowledge."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Result")

        repo = FindingsRepository(mock_model)

        repo.synthesize_findings(
            query="Query",
            sub_queries=[],
            findings=[],
            accumulated_knowledge="Existing knowledge",
        )

        # Verify that model was invoked
        mock_model.invoke.assert_called_once()

    @patch(
        "local_deep_research.advanced_search_system.findings.repository.format_findings"
    )
    def test_old_formatting_mode(self, mock_format):
        """Test old formatting mode."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_format.return_value = "Old formatted result"

        repo = FindingsRepository(Mock())
        findings = ["Finding 1"]

        result = repo.synthesize_findings(
            query="Query",
            sub_queries=[],
            findings=findings,
            old_formatting=True,
        )

        assert result == "Old formatted result"
        mock_format.assert_called_once()

    def test_handles_model_error(self):
        """Test handling model invocation error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("Model error")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "Error" in result

    def test_handles_timeout_error(self):
        """Test handling timeout error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("Request timed out")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "timeout" in result.lower() or "Error" in result

    def test_handles_token_limit_error(self):
        """Test handling token limit error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("Token limit exceeded")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "Error" in result

    def test_truncates_long_knowledge(self):
        """Test that very long knowledge is truncated."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Result")

        repo = FindingsRepository(mock_model)
        # Create very long finding content
        long_content = "A" * 30000
        findings = [{"content": long_content}]

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=findings
        )

        # Should still succeed
        assert result == "Result"

    def test_handles_string_response(self):
        """Test handling string response from model."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = "String response"

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "String response" in result


class TestEdgeCases:
    """Tests for edge cases."""

    def test_empty_findings_list(self):
        """Test with empty findings list."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Empty result")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[]
        )

        assert "Empty result" in result

    def test_mixed_findings_types(self):
        """Test with mixed finding types."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Mixed result")

        repo = FindingsRepository(mock_model)
        findings = [{"content": "Dict finding"}, "String finding"]

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=findings
        )

        assert result == "Mixed result"

    def test_handles_rate_limit_error(self):
        """Test handling rate limit error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("rate limit exceeded")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "Error" in result

    def test_handles_connection_error(self):
        """Test handling connection error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("connection refused")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "Error" in result

    def test_handles_api_error(self):
        """Test handling API error."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("API error 500")

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=[{"content": "Finding"}]
        )

        assert "Error" in result

    def test_synthesize_with_sub_queries(self):
        """Test synthesize with sub-queries."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(
            content="Synthesized with sub-queries"
        )

        repo = FindingsRepository(mock_model)

        result = repo.synthesize_findings(
            query="Main query",
            sub_queries=["Sub query 1", "Sub query 2"],
            findings=[{"content": "Finding"}],
        )

        # Verify sub-queries appear in the prompt
        call_args = mock_model.invoke.call_args[0][0]
        assert "Sub query 1" in call_args
        assert "Sub query 2" in call_args
        assert result == "Synthesized with sub-queries"

    def test_truncation_on_very_long_content(self):
        """Test that very long content is handled (either truncated or processed)."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Processed result")

        repo = FindingsRepository(mock_model)
        # Create content > 24000 chars
        long_content = "A" * 30000
        findings = [{"content": long_content}]

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=findings
        )

        # Verify the synthesis completes successfully
        assert result == "Processed result"
        # Verify model was invoked
        mock_model.invoke.assert_called_once()

    def test_synthesize_handles_dict_findings_with_missing_content(self):
        """Test synthesize with dict findings missing content key."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Result")

        repo = FindingsRepository(mock_model)
        findings = [{"phase": "Research"}, {"other_key": "value"}]

        result = repo.synthesize_findings(
            query="Query", sub_queries=[], findings=findings
        )

        assert result == "Result"

    def test_get_all_findings(self):
        """Test getting all findings."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        repo = FindingsRepository(mock_model)

        repo.add_finding("query1", "Finding 1")
        repo.add_finding("query2", "Finding 2")

        # Verify findings are stored
        assert len(repo.findings) == 2
        assert "query1" in repo.findings
        assert "query2" in repo.findings

    def test_questions_by_iteration_independence(self):
        """Test that questions_by_iteration copy is independent."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        repo = FindingsRepository(mock_model)

        questions = {1: ["Q1", "Q2"]}
        repo.set_questions_by_iteration(questions)

        # Modify original - should not affect repo
        questions[1].append("Q3")
        questions[2] = ["New"]

        # Dict itself is shallow copy so new keys won't be in repo
        assert 2 not in repo.questions_by_iteration
        # But list modification affects the repo (shallow copy)

    def test_format_links_preserves_order(self):
        """Test that format_links preserves order."""
        from local_deep_research.advanced_search_system.findings.repository import (
            format_links,
        )

        links = [
            {"title": "First", "url": "https://first.com"},
            {"title": "Second", "url": "https://second.com"},
            {"title": "Third", "url": "https://third.com"},
        ]

        result = format_links(links)

        # Check order is preserved
        first_pos = result.find("1. First")
        second_pos = result.find("2. Second")
        third_pos = result.find("3. Third")

        assert first_pos < second_pos < third_pos
