"""
Deep behavioral tests for TopicSubscription.
Tests topic evolution, merging, activity tracking, auto-expiry, and query generation.
"""

from datetime import datetime, timedelta, timezone
from unittest.mock import patch


from local_deep_research.news.core.base_card import CardSource


def _make_topic_sub(topic="AI", user_id="user1", **kwargs):
    """Create a TopicSubscription with mocked storage."""
    from local_deep_research.news.subscription_manager.topic_subscription import (
        TopicSubscription,
    )

    with patch(
        "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
    ):
        return TopicSubscription(topic=topic, user_id=user_id, **kwargs)


# --- Init ---


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

    def test_topic_stored(self):
        sub = _make_topic_sub(topic="Climate Change")
        assert sub.topic == "Climate Change"

    def test_current_topic_equals_topic(self):
        sub = _make_topic_sub(topic="AI")
        assert sub.current_topic == "AI"

    def test_topic_history_initialized(self):
        sub = _make_topic_sub(topic="AI")
        assert sub.topic_history == ["AI"]

    def test_related_topics_empty_default(self):
        sub = _make_topic_sub()
        assert sub.related_topics == []

    def test_related_topics_stored(self):
        sub = _make_topic_sub(related_topics=["ML", "Deep Learning"])
        assert sub.related_topics == ["ML", "Deep Learning"]

    def test_subscription_type_is_topic(self):
        sub = _make_topic_sub()
        assert sub.subscription_type == "topic"

    def test_activity_threshold_default(self):
        sub = _make_topic_sub()
        assert sub.activity_threshold == 3

    def test_metadata_includes_subscription_type(self):
        sub = _make_topic_sub()
        assert sub.metadata["subscription_type"] == "topic"

    def test_metadata_includes_original_topic(self):
        sub = _make_topic_sub(topic="Quantum")
        assert sub.metadata["original_topic"] == "Quantum"

    def test_metadata_not_trending_initially(self):
        sub = _make_topic_sub()
        assert sub.metadata["is_trending"] is False

    def test_auto_creates_source_if_none(self):
        sub = _make_topic_sub(topic="Test")
        assert sub.source.type == "news_topic"

    def test_uses_provided_source(self):
        source = CardSource(type="custom_source")
        sub = _make_topic_sub(source=source)
        assert sub.source.type == "custom_source"

    def test_get_subscription_type(self):
        sub = _make_topic_sub()
        assert sub.get_subscription_type() == "topic_subscription"


# --- Generate search query ---


class TestTopicSubscriptionQuery:
    """Tests for generate_search_query."""

    def test_includes_topic(self):
        sub = _make_topic_sub(topic="artificial intelligence")
        query = sub.generate_search_query()
        assert "artificial intelligence" in query

    def test_includes_related_topics(self):
        sub = _make_topic_sub(
            topic="AI",
            related_topics=["machine learning", "deep learning", "NLP"],
        )
        query = sub.generate_search_query()
        assert "machine learning" in query
        assert "deep learning" in query

    def test_limits_related_topics_to_2(self):
        sub = _make_topic_sub(
            topic="AI",
            related_topics=["ML", "DL", "NLP", "Computer Vision"],
        )
        query = sub.generate_search_query()
        # Only first 2 related topics included
        assert "ML" in query
        assert "DL" in query
        assert "Computer Vision" not in query

    def test_includes_news_terms(self):
        sub = _make_topic_sub(topic="AI")
        query = sub.generate_search_query()
        assert "news" in query.lower() or "developments" in query.lower()

    def test_uses_current_topic_not_original(self):
        sub = _make_topic_sub(topic="AI")
        sub.current_topic = "Artificial General Intelligence"
        query = sub.generate_search_query()
        assert "Artificial General Intelligence" in query


# --- Topic evolution ---


class TestTopicEvolution:
    """Tests for evolve_topic."""

    def test_updates_current_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("AGI")
        assert sub.current_topic == "AGI"

    def test_adds_to_history(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("AGI")
        assert sub.topic_history == ["AI", "AGI"]

    def test_multiple_evolutions(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("Machine Learning")
        sub.evolve_topic("Deep Learning")
        assert sub.topic_history == ["AI", "Machine Learning", "Deep Learning"]

    def test_no_evolution_if_same_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("AI")
        assert sub.topic_history == ["AI"]

    def test_metadata_tracks_evolution(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("AGI", reason="convergence")
        assert sub.metadata["last_evolution"]["from"] == "AI"
        assert sub.metadata["last_evolution"]["to"] == "AGI"
        assert sub.metadata["last_evolution"]["reason"] == "convergence"

    def test_original_topic_unchanged(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("AGI")
        assert sub.topic == "AI"


# --- Related topics ---


class TestAddRelatedTopic:
    """Tests for add_related_topic."""

    def test_adds_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.add_related_topic("ML")
        assert "ML" in sub.related_topics

    def test_no_duplicates(self):
        sub = _make_topic_sub(topic="AI")
        sub.add_related_topic("ML")
        sub.add_related_topic("ML")
        assert sub.related_topics.count("ML") == 1

    def test_does_not_add_current_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.add_related_topic("AI")
        assert "AI" not in sub.related_topics


# --- Merge ---


class TestMergeSubscriptions:
    """Tests for merge_with."""

    def test_adds_other_topic_as_related(self):
        sub1 = _make_topic_sub(topic="AI")
        sub2 = _make_topic_sub(topic="ML")
        sub1.merge_with(sub2)
        assert "ML" in sub1.related_topics

    def test_merges_related_topics(self):
        sub1 = _make_topic_sub(topic="AI")
        sub2 = _make_topic_sub(topic="ML", related_topics=["DL", "NLP"])
        sub1.merge_with(sub2)
        assert "DL" in sub1.related_topics
        assert "NLP" in sub1.related_topics

    def test_metadata_tracks_merge(self):
        sub1 = _make_topic_sub(topic="AI")
        sub2 = _make_topic_sub(topic="ML", subscription_id="sub-ml")
        sub1.merge_with(sub2)
        assert sub1.metadata["merged_from"]["topic"] == "ML"
        assert sub1.metadata["merged_from"]["subscription_id"] == "sub-ml"


# --- Activity tracking ---


class TestUpdateActivity:
    """Tests for update_activity."""

    def test_marks_trending_when_above_threshold(self):
        sub = _make_topic_sub()
        sub.update_activity(5)
        assert sub.metadata["is_trending"] is True

    def test_marks_trending_on_significant_news(self):
        sub = _make_topic_sub()
        sub.update_activity(1, significant_news=True)
        assert sub.metadata["is_trending"] is True

    def test_stays_trending_below_threshold_recently(self):
        sub = _make_topic_sub()
        sub.metadata["is_trending"] = True
        # Recent activity - should stay trending
        sub.update_activity(1)
        assert sub.metadata["is_trending"] is True

    def test_stops_trending_after_72_hours(self):
        sub = _make_topic_sub()
        sub.metadata["is_trending"] = True
        sub.last_significant_activity = datetime.now(timezone.utc) - timedelta(
            hours=73
        )
        sub.update_activity(1)
        assert sub.metadata["is_trending"] is False


# --- Auto expiry ---


class TestShouldAutoExpire:
    """Tests for should_auto_expire."""

    def test_does_not_expire_when_errors(self):
        sub = _make_topic_sub()
        sub.error_count = 1
        assert sub.should_auto_expire() is False

    def test_does_not_expire_when_no_refreshes(self):
        sub = _make_topic_sub()
        sub.error_count = 0
        sub.refresh_count = 0
        assert sub.should_auto_expire() is False

    def test_does_not_expire_when_active_recently(self):
        sub = _make_topic_sub()
        sub.error_count = 0
        sub.refresh_count = 5
        sub.last_significant_activity = datetime.now(timezone.utc) - timedelta(
            days=10
        )
        assert sub.should_auto_expire() is False

    def test_expires_after_30_days_inactive(self):
        sub = _make_topic_sub()
        sub.error_count = 0
        sub.refresh_count = 5
        sub.last_significant_activity = datetime.now(timezone.utc) - timedelta(
            days=31
        )
        assert sub.should_auto_expire() is True


# --- Statistics ---


class TestTopicSubscriptionStatistics:
    """Tests for get_statistics."""

    def test_includes_original_topic(self):
        sub = _make_topic_sub(topic="AI")
        stats = sub.get_statistics()
        assert stats["original_topic"] == "AI"

    def test_includes_current_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.current_topic = "AGI"
        stats = sub.get_statistics()
        assert stats["current_topic"] == "AGI"

    def test_evolution_count(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("ML")
        sub.evolve_topic("DL")
        stats = sub.get_statistics()
        assert stats["evolution_count"] == 2

    def test_related_topics_count(self):
        sub = _make_topic_sub(topic="AI", related_topics=["ML", "DL"])
        stats = sub.get_statistics()
        assert stats["related_topics_count"] == 2

    def test_is_trending(self):
        sub = _make_topic_sub()
        sub.metadata["is_trending"] = True
        stats = sub.get_statistics()
        assert stats["is_trending"] is True

    def test_days_since_activity(self):
        sub = _make_topic_sub()
        stats = sub.get_statistics()
        assert stats["days_since_activity"] >= 0

    def test_total_refreshes(self):
        sub = _make_topic_sub()
        sub.refresh_count = 42
        stats = sub.get_statistics()
        assert stats["total_refreshes"] == 42


# --- to_dict ---


class TestTopicSubscriptionToDict:
    """Tests for to_dict serialization."""

    def test_includes_topic(self):
        sub = _make_topic_sub(topic="AI")
        d = sub.to_dict()
        assert d["topic"] == "AI"

    def test_includes_current_topic(self):
        sub = _make_topic_sub(topic="AI")
        sub.current_topic = "AGI"
        d = sub.to_dict()
        assert d["current_topic"] == "AGI"

    def test_includes_related_topics(self):
        sub = _make_topic_sub(related_topics=["ML"])
        d = sub.to_dict()
        assert d["related_topics"] == ["ML"]

    def test_includes_topic_history(self):
        sub = _make_topic_sub(topic="AI")
        sub.evolve_topic("ML")
        d = sub.to_dict()
        assert d["topic_history"] == ["AI", "ML"]

    def test_includes_statistics(self):
        sub = _make_topic_sub()
        d = sub.to_dict()
        assert "statistics" in d

    def test_includes_base_fields(self):
        sub = _make_topic_sub()
        d = sub.to_dict()
        assert "id" in d
        assert "user_id" in d
        assert "created_at" in d


# --- Factory ---


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

    def test_from_news_extraction(self):
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        with patch(
            "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
        ):
            sub = TopicSubscriptionFactory.from_news_extraction(
                user_id="u1",
                topic="AI",
                source_news_id="news-123",
            )
        assert sub.topic == "AI"
        assert sub.source.type == "news_topic"
        assert sub.source.source_id == "news-123"

    def test_from_user_interest(self):
        from local_deep_research.news.subscription_manager.topic_subscription import (
            TopicSubscriptionFactory,
        )

        with patch(
            "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
        ):
            sub = TopicSubscriptionFactory.from_user_interest(
                user_id="u1",
                topic="quantum computing",
            )
        assert sub.topic == "quantum computing"
        assert sub.source.type == "user_interest"
