"""
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

from langchain_core.documents import Document


class TestFormatLinks:
    """Tests for format_links function."""

    def test_formats_single_link(self):
        """Test formatting a single link."""
        from local_deep_research.advanced_search_system.findings.repository import (
            format_links,
        )

        links = [{"title": "Test Page", "url": "https://example.com"}]

        result = format_links(links)

        assert "1. Test Page" in result
        assert "https://example.com" in result

    def test_formats_multiple_links(self):
        """Test formatting multiple links."""
        from local_deep_research.advanced_search_system.findings.repository import (
            format_links,
        )

        links = [
            {"title": "Page 1", "url": "https://example1.com"},
            {"title": "Page 2", "url": "https://example2.com"},
        ]

        result = format_links(links)

        assert "1. Page 1" in result
        assert "2. Page 2" in result

    def test_handles_empty_list(self):
        """Test handling empty list."""
        from local_deep_research.advanced_search_system.findings.repository import (
            format_links,
        )

        result = format_links([])

        assert result == ""


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 TestAddFinding:
    """Tests for add_finding method."""

    def test_adds_string_finding(self):
        """Test adding a string finding."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        repo.add_finding("test query", "This is a finding")

        findings = repo.findings["test query"]
        assert len(findings) == 1
        assert findings[0]["content"] == "This is a finding"
        assert findings[0]["phase"] == "Synthesis"

    def test_adds_dict_finding(self):
        """Test adding a dictionary finding."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        finding = {"phase": "Research", "content": "Dict finding"}
        repo.add_finding("test query", finding)

        findings = repo.findings["test query"]
        assert len(findings) == 1
        assert findings[0]["phase"] == "Research"

    def test_creates_synthesis_for_final_synthesis(self):
        """Test that final synthesis creates a copy."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        finding = {"phase": "Final synthesis", "content": "Final content"}
        repo.add_finding("test query", finding)

        assert "test query_synthesis" in repo.findings

    def test_appends_multiple_findings(self):
        """Test appending multiple findings for same query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        repo.add_finding("query", "Finding 1")
        repo.add_finding("query", "Finding 2")

        assert len(repo.findings["query"]) == 2


class TestGetFindings:
    """Tests for get_findings method."""

    def test_returns_findings_for_query(self):
        """Test getting findings for existing query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        repo.add_finding("query", "Finding")

        result = repo.get_findings("query")

        assert len(result) == 1

    def test_returns_empty_for_unknown_query(self):
        """Test getting findings for unknown query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())

        result = repo.get_findings("unknown")

        assert result == []


class TestClearFindings:
    """Tests for clear_findings method."""

    def test_clears_findings_for_query(self):
        """Test clearing findings for a query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        repo.add_finding("query", "Finding")
        repo.clear_findings("query")

        assert "query" not in repo.findings

    def test_handles_nonexistent_query(self):
        """Test clearing findings for nonexistent query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        # Should not raise
        repo.clear_findings("nonexistent")


class TestAddDocuments:
    """Tests for add_documents method."""

    def test_adds_documents(self):
        """Test adding documents."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        docs = [Document(page_content="Doc 1"), Document(page_content="Doc 2")]

        repo.add_documents(docs)

        assert len(repo.documents) == 2

    def test_extends_existing_documents(self):
        """Test that documents are extended, not replaced."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        repo.add_documents([Document(page_content="Doc 1")])
        repo.add_documents([Document(page_content="Doc 2")])

        assert len(repo.documents) == 2


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

    def test_sets_questions(self):
        """Test setting questions by iteration."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        repo = FindingsRepository(Mock())
        questions = {1: ["Q1", "Q2"], 2: ["Q3"]}

        repo.set_questions_by_iteration(questions)

        assert repo.questions_by_iteration == questions

    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_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_clear_all_findings(self):
        """Test clearing 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")

        repo.clear_findings("query1")
        repo.clear_findings("query2")

        assert "query1" not in repo.findings
        assert "query2" not in repo.findings

    def test_documents_extend_properly(self):
        """Test that documents are properly extended."""
        from langchain_core.documents import Document

        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        repo = FindingsRepository(mock_model)

        docs1 = [Document(page_content="Doc 1")]
        docs2 = [Document(page_content="Doc 2")]
        docs3 = [Document(page_content="Doc 3")]

        repo.add_documents(docs1)
        repo.add_documents(docs2)
        repo.add_documents(docs3)

        assert len(repo.documents) == 3

    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_synthesize_with_only_string_findings(self):
        """Test synthesize with all string findings."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="String findings result")

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

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

        assert result == "String findings result"

    def test_add_finding_creates_entry_for_new_query(self):
        """Test that add_finding creates new entry for new query."""
        from local_deep_research.advanced_search_system.findings.repository import (
            FindingsRepository,
        )

        mock_model = Mock()
        repo = FindingsRepository(mock_model)

        assert "new_query" not in repo.findings

        repo.add_finding("new_query", "Finding content")

        assert "new_query" in repo.findings
        assert len(repo.findings["new_query"]) == 1

    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
