"""
Extended tests for news/subscription_manager/topic_subscription.py

Tests cover:
- TopicSubscription initialization and defaults
- generate_search_query() - query generation with related topics
- update_activity() - activity tracking and trending detection
- evolve_topic() - topic evolution and history tracking
- add_related_topic() - related topic management
- merge_with() - subscription merging
- should_auto_expire() - expiration logic
- get_statistics() - statistics calculation
- TopicSubscriptionFactory - factory methods
"""

import pytest
from unittest.mock import patch, MagicMock
from datetime import timedelta


@pytest.fixture(autouse=True)
def mock_storage():
    """Mock SQLSubscriptionStorage for all tests."""
    with patch(
        "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
    ) as mock:
        mock.return_value = MagicMock()
        yield mock


class TestTopicSubscriptionInit:
    """Tests for TopicSubscription initialization."""

    def test_creates_with_required_args(self):
        """Creates subscription with just topic and user_id."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="AI news", user_id="user123")

        assert sub.topic == "AI news"
        assert sub.user_id == "user123"

    def test_default_refresh_interval_is_240_minutes(self):
        """Default refresh interval is 4 hours (240 minutes)."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.refresh_interval_minutes == 240

    def test_custom_refresh_interval(self):
        """Custom refresh interval is respected."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="test", user_id="user1", refresh_interval_minutes=60
        )

        assert sub.refresh_interval_minutes == 60

    def test_auto_creates_source_when_not_provided(self):
        """Auto-creates CardSource when not provided."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="climate change", user_id="user1")

        assert sub.source is not None
        assert sub.source.type == "news_topic"
        assert "climate change" in sub.source.created_from

    def test_uses_provided_source(self):
        """Uses provided CardSource."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.base_card import CardSource

        custom_source = CardSource(type="custom", source_id="src-1")
        sub = TopicSubscription(
            topic="test", user_id="user1", source=custom_source
        )

        assert sub.source.type == "custom"
        assert sub.source.source_id == "src-1"

    def test_related_topics_defaults_to_empty_list(self):
        """Related topics defaults to empty list."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.related_topics == []

    def test_accepts_related_topics(self):
        """Accepts related topics list."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        related = ["machine learning", "neural networks"]
        sub = TopicSubscription(
            topic="AI", user_id="user1", related_topics=related
        )

        assert sub.related_topics == related

    def test_subscription_type_is_topic(self):
        """Subscription type is set to 'topic'."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.subscription_type == "topic"

    def test_initializes_topic_history(self):
        """Topic history is initialized with current topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original topic", user_id="user1")

        assert sub.topic_history == ["original topic"]
        assert sub.current_topic == "original topic"

    def test_activity_threshold_default(self):
        """Activity threshold defaults to 3."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.activity_threshold == 3

    def test_metadata_includes_subscription_type(self):
        """Metadata includes subscription type."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.metadata["subscription_type"] == "topic"
        assert sub.metadata["original_topic"] == "test"

    def test_get_subscription_type_returns_topic_subscription(self):
        """get_subscription_type returns 'topic_subscription'."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")

        assert sub.get_subscription_type() == "topic_subscription"


class TestGenerateSearchQuery:
    """Tests for TopicSubscription.generate_search_query()."""

    def test_includes_current_topic(self):
        """Query includes the current topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="climate change", user_id="user1")
        query = sub.generate_search_query()

        assert "climate change" in query

    def test_wraps_topic_in_quotes(self):
        """Topic is wrapped in quotes for exact matching."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="artificial intelligence", user_id="user1"
        )
        query = sub.generate_search_query()

        assert '"artificial intelligence"' in query

    def test_includes_related_topics_with_or(self):
        """Related topics are included with OR operator."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="AI",
            user_id="user1",
            related_topics=["machine learning", "deep learning"],
        )
        query = sub.generate_search_query()

        assert "OR" in query
        assert '"machine learning"' in query

    def test_limits_related_topics_to_two(self):
        """Only includes up to 2 related topics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        many_related = ["topic1", "topic2", "topic3", "topic4", "topic5"]
        sub = TopicSubscription(
            topic="main", user_id="user1", related_topics=many_related
        )
        query = sub.generate_search_query()

        # Should have main topic + 2 related = 3 quoted terms
        assert query.count('"') == 6  # 3 terms * 2 quotes each

    def test_adds_news_terms(self):
        """Adds news-related terms to query."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        query = sub.generate_search_query()

        assert "news" in query.lower()

    def test_replaces_date_placeholder(self):
        """Replaces YYYY-MM-DD placeholder with current date."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.current_topic = "events on YYYY-MM-DD"

        query = sub.generate_search_query()

        assert "YYYY-MM-DD" not in query

    def test_uses_current_topic_not_original(self):
        """Uses current_topic, not original topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")
        sub.current_topic = "evolved topic"

        query = sub.generate_search_query()

        assert "evolved topic" in query


class TestUpdateActivity:
    """Tests for TopicSubscription.update_activity()."""

    def test_marks_trending_when_above_threshold(self):
        """Marks as trending when news_count >= activity_threshold."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.activity_threshold = 3
        sub.metadata["is_trending"] = False

        sub.update_activity(news_count=5)

        assert sub.metadata["is_trending"] is True

    def test_marks_trending_at_threshold(self):
        """Marks as trending when news_count equals threshold."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.activity_threshold = 3

        sub.update_activity(news_count=3)

        assert sub.metadata["is_trending"] is True

    def test_marks_trending_for_significant_news(self):
        """Marks as trending when significant_news=True even with low count."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.activity_threshold = 10

        sub.update_activity(news_count=1, significant_news=True)

        assert sub.metadata["is_trending"] is True

    def test_updates_last_significant_activity(self):
        """Updates last_significant_activity when above threshold."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        old_activity = sub.last_significant_activity

        # Small delay to ensure time difference
        import time

        time.sleep(0.01)

        sub.update_activity(news_count=5)

        assert sub.last_significant_activity >= old_activity

    def test_removes_trending_after_72_hours_inactive(self):
        """Removes trending status after 72 hours of inactivity."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.metadata["is_trending"] = True
        sub.last_significant_activity = utc_now() - timedelta(hours=73)

        sub.update_activity(news_count=1)  # Below threshold

        assert sub.metadata["is_trending"] is False

    def test_keeps_trending_within_72_hours(self):
        """Keeps trending status within 72 hours even with low activity."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.metadata["is_trending"] = True
        sub.last_significant_activity = utc_now() - timedelta(hours=24)

        sub.update_activity(news_count=1)  # Below threshold

        # Should stay True since still within 72 hours
        assert sub.metadata["is_trending"] is True


class TestEvolveTopic:
    """Tests for TopicSubscription.evolve_topic()."""

    def test_updates_current_topic(self):
        """Updates current_topic to new form."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")

        sub.evolve_topic("evolved form")

        assert sub.current_topic == "evolved form"

    def test_adds_to_topic_history(self):
        """Adds new form to topic_history."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")

        sub.evolve_topic("evolved form")

        assert sub.topic_history == ["original", "evolved form"]

    def test_records_evolution_metadata(self):
        """Records evolution details in metadata."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")

        sub.evolve_topic("evolved form", reason="trend shift")

        assert sub.metadata["last_evolution"]["from"] == "original"
        assert sub.metadata["last_evolution"]["to"] == "evolved form"
        assert sub.metadata["last_evolution"]["reason"] == "trend shift"

    def test_no_change_when_same_topic(self):
        """Does not add to history when topic is the same."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="unchanged", user_id="user1")

        sub.evolve_topic("unchanged")

        assert len(sub.topic_history) == 1

    def test_multiple_evolutions(self):
        """Tracks multiple evolutions correctly."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="v1", user_id="user1")

        sub.evolve_topic("v2")
        sub.evolve_topic("v3")
        sub.evolve_topic("v4")

        assert sub.topic_history == ["v1", "v2", "v3", "v4"]
        assert sub.current_topic == "v4"


class TestAddRelatedTopic:
    """Tests for TopicSubscription.add_related_topic()."""

    def test_adds_new_related_topic(self):
        """Adds new topic to related_topics list."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="AI", user_id="user1")

        sub.add_related_topic("machine learning")

        assert "machine learning" in sub.related_topics

    def test_does_not_add_duplicate(self):
        """Does not add duplicate related topics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="AI", user_id="user1", related_topics=["machine learning"]
        )

        sub.add_related_topic("machine learning")

        assert sub.related_topics.count("machine learning") == 1

    def test_does_not_add_current_topic(self):
        """Does not add the current topic as related."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="AI", user_id="user1")

        sub.add_related_topic("AI")

        assert "AI" not in sub.related_topics


class TestMergeWith:
    """Tests for TopicSubscription.merge_with()."""

    def test_adds_other_topic_as_related(self):
        """Adds the other subscription's topic as related."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub1 = TopicSubscription(topic="AI", user_id="user1")
        sub2 = TopicSubscription(topic="Machine Learning", user_id="user1")

        sub1.merge_with(sub2)

        assert "Machine Learning" in sub1.related_topics

    def test_merges_related_topics(self):
        """Merges related topics from both subscriptions."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub1 = TopicSubscription(
            topic="AI", user_id="user1", related_topics=["automation"]
        )
        sub2 = TopicSubscription(
            topic="ML", user_id="user1", related_topics=["neural networks"]
        )

        sub1.merge_with(sub2)

        assert "automation" in sub1.related_topics
        assert "neural networks" in sub1.related_topics
        assert "ML" in sub1.related_topics

    def test_records_merge_metadata(self):
        """Records merge information in metadata."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub1 = TopicSubscription(topic="AI", user_id="user1")
        sub2 = TopicSubscription(topic="ML", user_id="user1")

        sub1.merge_with(sub2)

        assert sub1.metadata["merged_from"]["topic"] == "ML"
        assert "subscription_id" in sub1.metadata["merged_from"]
        assert "timestamp" in sub1.metadata["merged_from"]


class TestShouldAutoExpire:
    """Tests for TopicSubscription.should_auto_expire()."""

    def test_returns_false_with_recent_activity(self):
        """Returns False when there's recent activity."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.refresh_count = 5
        sub.error_count = 0
        sub.last_significant_activity = utc_now() - timedelta(days=5)

        assert sub.should_auto_expire() is False

    def test_returns_true_after_30_days_inactive(self):
        """Returns True after 30 days of no significant activity."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.refresh_count = 5
        sub.error_count = 0
        sub.last_significant_activity = utc_now() - timedelta(days=31)

        assert sub.should_auto_expire() is True

    def test_returns_false_with_errors(self):
        """Returns False when there are errors (hasn't been refreshing successfully)."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.refresh_count = 0
        sub.error_count = 5
        sub.last_significant_activity = utc_now() - timedelta(days=60)

        assert sub.should_auto_expire() is False

    def test_returns_false_with_no_refreshes(self):
        """Returns False when never refreshed."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.refresh_count = 0
        sub.error_count = 0
        sub.last_significant_activity = utc_now() - timedelta(days=60)

        assert sub.should_auto_expire() is False


class TestGetStatistics:
    """Tests for TopicSubscription.get_statistics()."""

    def test_includes_original_topic(self):
        """Statistics include original topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")
        stats = sub.get_statistics()

        assert stats["original_topic"] == "original"

    def test_includes_current_topic(self):
        """Statistics include current topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")
        sub.current_topic = "evolved"
        stats = sub.get_statistics()

        assert stats["current_topic"] == "evolved"

    def test_counts_evolutions(self):
        """Counts topic evolutions correctly."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="v1", user_id="user1")
        sub.evolve_topic("v2")
        sub.evolve_topic("v3")

        stats = sub.get_statistics()

        assert stats["evolution_count"] == 2

    def test_counts_related_topics(self):
        """Counts related topics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="main", user_id="user1", related_topics=["a", "b", "c"]
        )

        stats = sub.get_statistics()

        assert stats["related_topics_count"] == 3

    def test_includes_trending_status(self):
        """Includes trending status."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.metadata["is_trending"] = True

        stats = sub.get_statistics()

        assert stats["is_trending"] is True

    def test_calculates_days_since_activity(self):
        """Calculates days since last significant activity."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )
        from local_deep_research.news.core.utils import utc_now

        sub = TopicSubscription(topic="test", user_id="user1")
        sub.last_significant_activity = utc_now() - timedelta(days=5)

        stats = sub.get_statistics()

        assert 4.9 < stats["days_since_activity"] < 5.1


class TestToDict:
    """Tests for TopicSubscription.to_dict()."""

    def test_includes_topic(self):
        """Dict includes topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test topic", user_id="user1")
        data = sub.to_dict()

        assert data["topic"] == "test topic"

    def test_includes_current_topic(self):
        """Dict includes current_topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="original", user_id="user1")
        sub.current_topic = "current"
        data = sub.to_dict()

        assert data["current_topic"] == "current"

    def test_includes_related_topics(self):
        """Dict includes related_topics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(
            topic="main", user_id="user1", related_topics=["a"]
        )
        data = sub.to_dict()

        assert data["related_topics"] == ["a"]

    def test_includes_topic_history(self):
        """Dict includes topic_history."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="v1", user_id="user1")
        sub.evolve_topic("v2")
        data = sub.to_dict()

        assert data["topic_history"] == ["v1", "v2"]

    def test_includes_statistics(self):
        """Dict includes statistics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscription,
        )

        sub = TopicSubscription(topic="test", user_id="user1")
        data = sub.to_dict()

        assert "statistics" in data


class TestTopicSubscriptionFactory:
    """Tests for TopicSubscriptionFactory."""

    def test_from_news_extraction_creates_subscription(self):
        """from_news_extraction creates a valid subscription."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        sub = TopicSubscriptionFactory.from_news_extraction(
            user_id="user1",
            topic="AI breakthrough",
            source_news_id="news-123",
        )

        assert sub.topic == "AI breakthrough"
        assert sub.user_id == "user1"
        assert sub.source.source_id == "news-123"

    def test_from_news_extraction_sets_source_type(self):
        """from_news_extraction sets source type to news_topic."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        sub = TopicSubscriptionFactory.from_news_extraction(
            user_id="user1",
            topic="test",
            source_news_id="news-1",
        )

        assert sub.source.type == "news_topic"

    def test_from_news_extraction_includes_related_topics(self):
        """from_news_extraction includes related topics."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        related = ["topic1", "topic2"]
        sub = TopicSubscriptionFactory.from_news_extraction(
            user_id="user1",
            topic="main",
            source_news_id="news-1",
            related_topics=related,
        )

        assert sub.related_topics == related

    def test_from_user_interest_creates_subscription(self):
        """from_user_interest creates a valid subscription."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        sub = TopicSubscriptionFactory.from_user_interest(
            user_id="user1",
            topic="cybersecurity",
        )

        assert sub.topic == "cybersecurity"
        assert sub.source.type == "user_interest"

    def test_from_user_interest_records_default_created_via(self):
        """from_user_interest records default 'manual' when no created_via specified."""
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        sub = TopicSubscriptionFactory.from_user_interest(
            user_id="user1",
            topic="test",
        )

        # Default created_via is "manual"
        assert sub.source.metadata["created_via"] == "manual"
