"""
Tests for NewsAggregationStrategy pure logic methods.

Existing tests: none — this file is the first test coverage.

Covers:
- _format_news_summary: category grouping, top story selection, empty input,
  missing fields, impact scoring, top-3 per category limit
- _fallback_news_extraction: title filtering, headline truncation, snippet
  fallback, max 10 limit, empty/short title filtering
- _create_news_analysis_prompt: snippet joining, field mapping, date inclusion
"""

from unittest.mock import Mock

from freezegun import freeze_time


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

    mock_model = Mock()
    mock_search = Mock()
    return NewsAggregationStrategy(model=mock_model, search=mock_search)


class TestFormatNewsSummaryEmpty:
    """Tests for _format_news_summary with empty/missing input."""

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

    def test_none_like_empty(self):
        """Falsy input returns 'no stories' message."""
        strategy = _make_strategy()
        result = strategy._format_news_summary([])
        assert "No significant news stories" in result


class TestFormatNewsSummaryCategoryGrouping:
    """Tests for category grouping in _format_news_summary."""

    def test_single_category_single_item(self):
        """Single item in single category."""
        strategy = _make_strategy()
        items = [
            {
                "headline": "Earthquake hits region",
                "category": "Environment",
                "impact_score": 8,
            }
        ]
        result = strategy._format_news_summary(items)
        assert "**Environment** (1 stories):" in result
        assert "Earthquake hits region" in result
        assert "(Impact: 8/10)" in result

    def test_multiple_categories(self):
        """Items are grouped by category."""
        strategy = _make_strategy()
        items = [
            {
                "headline": "Market crash",
                "category": "Economy",
                "impact_score": 9,
            },
            {
                "headline": "New vaccine",
                "category": "Health",
                "impact_score": 7,
            },
            {
                "headline": "Trade deal",
                "category": "Economy",
                "impact_score": 6,
            },
        ]
        result = strategy._format_news_summary(items)
        assert "**Economy** (2 stories):" in result
        assert "**Health** (1 stories):" in result

    def test_missing_category_defaults_to_other(self):
        """Items without category key default to 'Other'."""
        strategy = _make_strategy()
        items = [{"headline": "Unknown event", "impact_score": 5}]
        result = strategy._format_news_summary(items)
        assert "**Other** (1 stories):" in result

    def test_top_3_per_category_limit(self):
        """Only top 3 items per category are shown."""
        strategy = _make_strategy()
        items = [
            {"headline": f"Story {i}", "category": "Tech", "impact_score": i}
            for i in range(5)
        ]
        result = strategy._format_news_summary(items)
        # Should show first 3 items (Story 0, 1, 2), not 4 and 5
        assert "Story 0" in result
        assert "Story 1" in result
        assert "Story 2" in result
        # Story 3 and 4 should not appear as bullet points
        lines = [
            line for line in result.split("\n") if line.startswith("- Story")
        ]
        assert len(lines) == 3


class TestFormatNewsSummaryTopStory:
    """Tests for top story selection in _format_news_summary."""

    def test_top_story_highest_impact(self):
        """Top story is the one with highest impact_score."""
        strategy = _make_strategy()
        items = [
            {"headline": "Minor event", "category": "Other", "impact_score": 3},
            {
                "headline": "Major crisis",
                "category": "Security",
                "impact_score": 10,
            },
            {
                "headline": "Medium event",
                "category": "Other",
                "impact_score": 6,
            },
        ]
        result = strategy._format_news_summary(items)
        assert "**Top Story**: Major crisis" in result

    def test_top_story_missing_impact_defaults_to_zero(self):
        """Items without impact_score are treated as 0."""
        strategy = _make_strategy()
        items = [
            {"headline": "No score", "category": "Other"},
            {"headline": "Has score", "category": "Other", "impact_score": 1},
        ]
        result = strategy._format_news_summary(items)
        assert "**Top Story**: Has score" in result

    def test_top_story_summary_included(self):
        """Top story summary is included."""
        strategy = _make_strategy()
        items = [
            {
                "headline": "Big news",
                "category": "Economy",
                "impact_score": 9,
                "summary": "Markets fell sharply today.",
            }
        ]
        result = strategy._format_news_summary(items)
        assert "Markets fell sharply today." in result

    def test_top_story_missing_summary(self):
        """Missing summary falls back to default."""
        strategy = _make_strategy()
        items = [
            {
                "headline": "No summary story",
                "category": "Other",
                "impact_score": 5,
            }
        ]
        result = strategy._format_news_summary(items)
        assert "No summary available" in result

    def test_found_count_in_header(self):
        """Header shows correct count of news items."""
        strategy = _make_strategy()
        items = [
            {"headline": f"Story {i}", "category": "Tech", "impact_score": i}
            for i in range(4)
        ]
        result = strategy._format_news_summary(items)
        assert "Found 4 significant news stories:" in result

    def test_impact_score_na_when_missing(self):
        """Impact shows N/A when score is missing."""
        strategy = _make_strategy()
        items = [{"headline": "No impact", "category": "Other"}]
        result = strategy._format_news_summary(items)
        assert "(Impact: N/A/10)" in result


class TestFallbackNewsExtraction:
    """Tests for _fallback_news_extraction."""

    def test_basic_extraction(self):
        """Extracts items from snippets with valid titles."""
        strategy = _make_strategy()
        snippets = [
            {
                "title": "A significant headline here",
                "url": "https://ex.com",
                "snippet": "Details here",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert result["status"] == "Fallback extraction"
        assert len(result["news_items"]) == 1
        assert (
            result["news_items"][0]["headline"] == "A significant headline here"
        )
        assert result["news_items"][0]["source_url"] == "https://ex.com"

    def test_short_title_filtered_out(self):
        """Titles <= 10 chars are filtered out."""
        strategy = _make_strategy()
        snippets = [
            {"title": "Short", "url": "https://a.com", "snippet": "Content"},
            {
                "title": "0123456789",
                "url": "https://b.com",
                "snippet": "Content",
            },  # exactly 10
            {
                "title": "Long enough title here",
                "url": "https://c.com",
                "snippet": "Content",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 1
        assert result["news_items"][0]["headline"] == "Long enough title here"

    def test_empty_title_filtered_out(self):
        """Empty string title is filtered out."""
        strategy = _make_strategy()
        snippets = [
            {"title": "", "url": "https://a.com", "snippet": "Content"},
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 0

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

    def test_max_10_snippets(self):
        """Only first 10 snippets are processed."""
        strategy = _make_strategy()
        snippets = [
            {
                "title": f"Valid title number {i:02d}",
                "url": f"https://ex{i}.com",
                "snippet": f"Snippet {i}",
            }
            for i in range(15)
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert len(result["news_items"]) == 10

    def test_snippet_fallback_to_default(self):
        """Empty snippet falls back to 'No summary available'."""
        strategy = _make_strategy()
        snippets = [
            {
                "title": "A valid title here",
                "url": "https://ex.com",
                "snippet": "",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert result["news_items"][0]["summary"] == "No summary available"

    def test_all_items_have_default_category_and_score(self):
        """All fallback items get category 'Other' and impact_score 5."""
        strategy = _make_strategy()
        snippets = [
            {
                "title": "A valid title here",
                "url": "https://ex.com",
                "snippet": "Content",
            },
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert result["news_items"][0]["category"] == "Other"
        assert result["news_items"][0]["impact_score"] == 5

    def test_answer_shows_correct_count(self):
        """Answer message shows correct count of extracted items."""
        strategy = _make_strategy()
        snippets = [
            {
                "title": f"Valid title number {i:02d}",
                "url": f"https://ex{i}.com",
                "snippet": f"S {i}",
            }
            for i in range(3)
        ]
        result = strategy._fallback_news_extraction(snippets)
        assert (
            "Found 3 news stories (simplified extraction)" == result["answer"]
        )

    def test_empty_snippets(self):
        """Empty snippets list returns zero items."""
        strategy = _make_strategy()
        result = strategy._fallback_news_extraction([])
        assert result["news_items"] == []
        assert "Found 0 news stories" in result["answer"]


class TestCreateNewsAnalysisPrompt:
    """Tests for _create_news_analysis_prompt."""

    @freeze_time("2025-06-15")
    def test_contains_date(self):
        """Prompt contains today's date."""
        strategy = _make_strategy()
        snippets = [
            {
                "id": 1,
                "url": "https://ex.com",
                "title": "Title",
                "snippet": "Snip",
                "content": "",
            }
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "June 15, 2025" in result

    def test_contains_snippet_fields(self):
        """Prompt includes all snippet fields."""
        strategy = _make_strategy()
        snippets = [
            {
                "id": 42,
                "url": "https://example.com/news",
                "title": "Breaking News",
                "snippet": "Details here",
                "content": "",
            }
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "[42] Source: https://example.com/news" in result
        assert "Title: Breaking News" in result
        assert "Content: Details here" in result

    def test_snippet_preferred_over_content(self):
        """Snippet is preferred over content when both are present."""
        strategy = _make_strategy()
        snippets = [
            {
                "id": 1,
                "url": "https://ex.com",
                "title": "T",
                "snippet": "snippet_text",
                "content": "content_text",
            }
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "Content: snippet_text" in result

    def test_content_fallback_when_no_snippet(self):
        """Content is used when snippet is empty."""
        strategy = _make_strategy()
        snippets = [
            {
                "id": 1,
                "url": "https://ex.com",
                "title": "T",
                "snippet": "",
                "content": "content_text",
            }
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "Content: content_text" in result

    def test_multiple_snippets_joined(self):
        """Multiple snippets are joined with double newlines."""
        strategy = _make_strategy()
        snippets = [
            {
                "id": 1,
                "url": "https://a.com",
                "title": "A",
                "snippet": "snip_a",
                "content": "",
            },
            {
                "id": 2,
                "url": "https://b.com",
                "title": "B",
                "snippet": "snip_b",
                "content": "",
            },
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "[1] Source: https://a.com" in result
        assert "[2] Source: https://b.com" in result

    def test_prompt_requests_json_response(self):
        """Prompt asks for structured JSON response."""
        strategy = _make_strategy()
        snippets = [
            {"id": 1, "url": "", "title": "T", "snippet": "S", "content": ""}
        ]
        result = strategy._create_news_analysis_prompt(snippets)
        assert "JSON response" in result
        assert "news_items" in result

    def test_empty_snippets(self):
        """Empty snippets list produces prompt with no snippet section."""
        strategy = _make_strategy()
        result = strategy._create_news_analysis_prompt([])
        # Should still have the prompt structure
        assert "news_items" in result
