"""
Tests for pure logic functions in search strategies.

These functions have zero existing test coverage despite being
testable without mocking LLMs or search engines:

- SourceBasedSearchStrategy._format_search_results_as_context()
- NewsAggregationStrategy._format_news_summary()
- NewsAggregationStrategy._fallback_news_extraction()
- BaseSearchStrategy.check_termination()
"""

from unittest.mock import Mock


# ---------------------------------------------------------------------------
# _format_search_results_as_context (source_based_strategy.py)
# ---------------------------------------------------------------------------


class TestFormatSearchResultsAsContext:
    """Tests for SourceBasedSearchStrategy._format_search_results_as_context()."""

    def _make_strategy(self):
        """Create a SourceBasedSearchStrategy with mocked dependencies."""
        from local_deep_research.advanced_search_system.strategies.source_based_strategy import (
            SourceBasedSearchStrategy,
        )

        model = Mock()
        search = Mock()
        strategy = SourceBasedSearchStrategy(
            model=model, search=search, all_links_of_system=[]
        )
        return strategy

    def test_empty_results(self):
        """Empty results list produces empty string."""
        strategy = self._make_strategy()
        result = strategy._format_search_results_as_context([])
        assert result == ""

    def test_single_result(self):
        """Single result formatted correctly."""
        strategy = self._make_strategy()
        results = [
            {
                "title": "Python Docs",
                "snippet": "Official documentation",
                "link": "https://docs.python.org",
            }
        ]
        result = strategy._format_search_results_as_context(results)
        assert "Source 1: Python Docs" in result
        assert "URL: https://docs.python.org" in result
        assert "Snippet: Official documentation" in result

    def test_multiple_results_numbered(self):
        """Multiple results numbered sequentially."""
        strategy = self._make_strategy()
        results = [
            {"title": "First", "snippet": "A", "link": "https://a.com"},
            {"title": "Second", "snippet": "B", "link": "https://b.com"},
            {"title": "Third", "snippet": "C", "link": "https://c.com"},
        ]
        result = strategy._format_search_results_as_context(results)
        assert "Source 1: First" in result
        assert "Source 2: Second" in result
        assert "Source 3: Third" in result

    def test_missing_title_uses_untitled(self):
        """Missing title defaults to 'Untitled'."""
        strategy = self._make_strategy()
        results = [{"snippet": "Content", "link": "https://example.com"}]
        result = strategy._format_search_results_as_context(results)
        assert "Source 1: Untitled" in result

    def test_missing_snippet_skips_result(self):
        """Result with empty snippet is skipped from output."""
        strategy = self._make_strategy()
        results = [
            {"title": "No Snippet", "snippet": "", "link": "https://a.com"},
            {
                "title": "Has Snippet",
                "snippet": "Content",
                "link": "https://b.com",
            },
        ]
        result = strategy._format_search_results_as_context(results)
        assert "No Snippet" not in result
        # Numbering uses original enumerate index, so second result is Source 2
        assert "Source 2: Has Snippet" in result

    def test_missing_link_uses_empty(self):
        """Missing link defaults to empty string."""
        strategy = self._make_strategy()
        results = [{"title": "Test", "snippet": "Content"}]
        result = strategy._format_search_results_as_context(results)
        assert "URL: \n" in result

    def test_results_separated_by_double_newline(self):
        """Results are separated by double newlines."""
        strategy = self._make_strategy()
        results = [
            {"title": "A", "snippet": "Content A", "link": "https://a.com"},
            {"title": "B", "snippet": "Content B", "link": "https://b.com"},
        ]
        result = strategy._format_search_results_as_context(results)
        assert "\n\n" in result

    def test_does_not_modify_input(self):
        """Method does not modify the input list or dicts."""
        strategy = self._make_strategy()
        results = [
            {"title": "Test", "snippet": "Content", "link": "https://a.com"}
        ]
        original_result = dict(results[0])
        strategy._format_search_results_as_context(results)
        assert results[0] == original_result


# ---------------------------------------------------------------------------
# _format_news_summary (news_strategy.py)
# ---------------------------------------------------------------------------


class TestFormatNewsSummary:
    """Tests for NewsAggregationStrategy._format_news_summary()."""

    def _make_strategy(self):
        """Create a NewsAggregationStrategy with mocked dependencies."""
        from local_deep_research.advanced_search_system.strategies.news_strategy import (
            NewsAggregationStrategy,
        )

        model = Mock()
        search = Mock()
        strategy = NewsAggregationStrategy(
            model=model, search=search, all_links_of_system=[]
        )
        return strategy

    def test_empty_items_returns_no_stories(self):
        """Empty list returns 'no stories' message."""
        strategy = self._make_strategy()
        result = strategy._format_news_summary([])
        assert "No significant news stories found" in result

    def test_single_item_formatted(self):
        """Single news item is formatted with headline and impact."""
        strategy = self._make_strategy()
        items = [
            {
                "headline": "Major Event",
                "category": "Politics",
                "impact_score": 8,
                "summary": "Something happened.",
            }
        ]
        result = strategy._format_news_summary(items)
        assert "Major Event" in result
        assert "8/10" in result
        assert "Politics" in result

    def test_groups_by_category(self):
        """Items are grouped by category."""
        strategy = self._make_strategy()
        items = [
            {"headline": "Tech News", "category": "Tech", "impact_score": 7},
            {"headline": "War Update", "category": "War", "impact_score": 9},
            {"headline": "More Tech", "category": "Tech", "impact_score": 6},
        ]
        result = strategy._format_news_summary(items)
        assert "**Tech** (2 stories)" in result
        assert "**War** (1 stories)" in result

    def test_top_story_highlighted(self):
        """Top story (highest impact) is highlighted."""
        strategy = self._make_strategy()
        items = [
            {"headline": "Minor Event", "category": "Other", "impact_score": 3},
            {
                "headline": "Major Event",
                "category": "War",
                "impact_score": 10,
                "summary": "Big thing happened.",
            },
        ]
        result = strategy._format_news_summary(items)
        assert "**Top Story**: Major Event" in result
        assert "Big thing happened." in result

    def test_max_three_per_category(self):
        """At most 3 items shown per category."""
        strategy = self._make_strategy()
        items = [
            {"headline": f"Item {i}", "category": "Tech", "impact_score": i}
            for i in range(5)
        ]
        result = strategy._format_news_summary(items)
        # Count lines starting with "- " under Tech
        tech_items = [
            line for line in result.split("\n") if line.startswith("- Item")
        ]
        assert len(tech_items) == 3

    def test_missing_impact_score_shows_na(self):
        """Missing impact_score shows N/A."""
        strategy = self._make_strategy()
        items = [
            {"headline": "No Score", "category": "Other"},
        ]
        result = strategy._format_news_summary(items)
        assert "N/A/10" in result

    def test_top_story_missing_summary(self):
        """Top story with missing summary shows fallback."""
        strategy = self._make_strategy()
        items = [
            {"headline": "No Summary", "category": "Other", "impact_score": 5},
        ]
        result = strategy._format_news_summary(items)
        assert "No summary available" in result

    def test_total_count_in_header(self):
        """Header includes total story count."""
        strategy = self._make_strategy()
        items = [
            {"headline": f"Story {i}", "category": "Other", "impact_score": i}
            for i in range(4)
        ]
        result = strategy._format_news_summary(items)
        assert "Found 4 significant news stories" in result


# ---------------------------------------------------------------------------
# _fallback_news_extraction (news_strategy.py)
# ---------------------------------------------------------------------------


class TestFallbackNewsExtraction:
    """Tests for NewsAggregationStrategy._fallback_news_extraction()."""

    def _make_strategy(self):
        from local_deep_research.advanced_search_system.strategies.news_strategy import (
            NewsAggregationStrategy,
        )

        model = Mock()
        search = Mock()
        return NewsAggregationStrategy(
            model=model, search=search, all_links_of_system=[]
        )

    def test_empty_snippets(self):
        """Empty snippets produces empty result."""
        strategy = self._make_strategy()
        result = strategy._fallback_news_extraction([])
        assert result["status"] == "Fallback extraction"
        assert result["news_items"] == []
        assert "0 news stories" in result["answer"]

    def test_extracts_from_valid_snippets(self):
        """Extracts news items from snippets with valid titles."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": 1,
                "title": "Major breakthrough in AI research announced today",
                "url": "https://example.com/ai",
                "snippet": "Scientists announced...",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 1
        item = result["news_items"][0]
        assert item["category"] == "Other"
        assert item["impact_score"] == 5
        assert item["source_url"] == "https://example.com/ai"

    def test_skips_short_titles(self):
        """Titles <= 10 characters are skipped."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": 1,
                "title": "Short",
                "url": "https://a.com",
                "snippet": "s",
                "content": "",
            },
            {
                "id": 2,
                "title": "1234567890",
                "url": "https://b.com",
                "snippet": "s",
                "content": "",
            },
            {
                "id": 3,
                "title": "Long enough title for news",
                "url": "https://c.com",
                "snippet": "ok",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 1
        assert result["news_items"][0]["source_url"] == "https://c.com"

    def test_skips_empty_titles(self):
        """Empty titles are skipped."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": 1,
                "title": "",
                "url": "https://a.com",
                "snippet": "content",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 0

    def test_truncates_long_headlines(self):
        """Headlines are truncated to 60 characters."""
        strategy = self._make_strategy()
        long_title = "A" * 100
        snippets = [
            {
                "id": 1,
                "title": long_title,
                "url": "https://a.com",
                "snippet": "s",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"][0]["headline"]) == 60

    def test_max_ten_items(self):
        """At most 10 items extracted."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": i,
                "title": f"Valid news headline number {i} here",
                "url": f"https://example{i}.com",
                "snippet": "content",
                "content": "",
            }
            for i in range(20)
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 10

    def test_uses_snippet_for_summary(self):
        """Snippet is used as summary."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": 1,
                "title": "Breaking news about something important",
                "url": "https://a.com",
                "snippet": "Detailed snippet content",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert result["news_items"][0]["summary"] == "Detailed snippet content"

    def test_fallback_summary_when_no_snippet(self):
        """Falls back to default summary when no snippet."""
        strategy = self._make_strategy()
        snippets = [
            {
                "id": 1,
                "title": "Breaking news about something important",
                "url": "https://a.com",
                "snippet": "",
                "content": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert result["news_items"][0]["summary"] == "No summary available"


# ---------------------------------------------------------------------------
# BaseSearchStrategy.check_termination() (base_strategy.py)
# ---------------------------------------------------------------------------


class TestCheckTermination:
    """Tests for BaseSearchStrategy.check_termination() method.

    This method was untested in the existing test_base_strategy.py.
    """

    def _make_strategy(self):
        from local_deep_research.advanced_search_system.strategies.base_strategy import (
            BaseSearchStrategy,
        )

        class ConcreteStrategy(BaseSearchStrategy):
            def analyze_topic(self, query):
                return {}

        return ConcreteStrategy()

    def test_check_termination_calls_callback(self):
        """check_termination() calls progress_callback with termination_check phase."""
        strategy = self._make_strategy()
        callback = Mock()
        strategy.set_progress_callback(callback)

        strategy.check_termination()

        callback.assert_called_once()
        args = callback.call_args[0]
        assert args[2]["phase"] == "termination_check"

    def test_check_termination_without_callback(self):
        """check_termination() does nothing without callback."""
        strategy = self._make_strategy()
        # Should not raise
        strategy.check_termination()

    def test_check_termination_passes_none_progress(self):
        """check_termination() passes None as progress_percent."""
        strategy = self._make_strategy()
        callback = Mock()
        strategy.set_progress_callback(callback)

        strategy.check_termination()

        args = callback.call_args[0]
        assert args[1] is None
