"""
Extended tests for card types (NewsCard, ResearchCard, UpdateCard, OverviewCard).
Tests dataclass attributes, methods, and type-specific behavior.
"""

from unittest.mock import Mock
from datetime import datetime, timezone


class TestCardSourceDataclass:
    """Tests for CardSource dataclass."""

    def test_creates_with_type(self):
        """Test creates with type parameter."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="news_item")
        assert source.type == "news_item"

    def test_default_source_id_is_none(self):
        """Test default source_id is None."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="news_item")
        assert source.source_id is None

    def test_default_created_from_is_empty_string(self):
        """Test default created_from is empty string."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="news_item")
        assert source.created_from == ""

    def test_default_metadata_is_empty_dict(self):
        """Test default metadata is empty dict."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="news_item")
        assert source.metadata == {}

    def test_custom_source_id(self):
        """Test setting custom source_id."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="subscription", source_id="sub-123")
        assert source.source_id == "sub-123"

    def test_custom_metadata(self):
        """Test setting custom metadata."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(
            type="news_item", metadata={"key": "value", "count": 42}
        )
        assert source.metadata["key"] == "value"
        assert source.metadata["count"] == 42


class TestCardVersionDataclass:
    """Tests for CardVersion dataclass."""

    def test_creates_with_required_fields(self):
        """Test creates with required fields."""
        from local_deep_research.news.core.base_card import CardVersion

        version = CardVersion(
            version_id="v1",
            created_at=datetime.now(timezone.utc),
            content={"data": "test"},
            query_used="test query",
        )
        assert version.version_id == "v1"
        assert version.query_used == "test query"

    def test_default_search_strategy_is_none(self):
        """Test default search_strategy is None."""
        from local_deep_research.news.core.base_card import CardVersion

        version = CardVersion(
            version_id="v1",
            created_at=datetime.now(timezone.utc),
            content={},
            query_used="query",
        )
        assert version.search_strategy is None

    def test_generates_version_id_if_empty(self):
        """Test generates version_id if empty string provided."""
        from local_deep_research.news.core.base_card import CardVersion

        version = CardVersion(
            version_id="",
            created_at=datetime.now(timezone.utc),
            content={},
            query_used="query",
        )
        # Should generate a UUID
        assert version.version_id != ""
        assert len(version.version_id) > 0


class TestNewsCardSpecifics:
    """Tests for NewsCard specific attributes."""

    def test_get_card_type_returns_news(self):
        """Test get_card_type returns 'news'."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")
        assert card.get_card_type() == "news"

    def test_default_headline_equals_topic(self):
        """Test default headline equals topic."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="My Topic", source=source, user_id="user1")
        assert card.headline == "My Topic"

    def test_custom_headline(self):
        """Test setting custom headline."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(
            topic="Topic",
            source=source,
            user_id="user1",
            headline="Custom Headline",
        )
        assert card.headline == "Custom Headline"

    def test_default_summary_is_empty(self):
        """Test default summary is empty string."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")
        assert card.summary == ""

    def test_default_category(self):
        """Test default category is 'General'."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")
        assert card.category == "General"

    def test_default_impact_score(self):
        """Test default impact_score is 5."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")
        assert card.impact_score == 5

    def test_default_is_developing(self):
        """Test default is_developing is False."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")
        assert card.is_developing is False

    def test_to_dict_includes_news_fields(self):
        """Test to_dict includes news-specific fields."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(
            topic="Test",
            source=source,
            user_id="user1",
            headline="Test Headline",
            category="Tech",
            impact_score=8,
        )
        result = card.to_dict()

        assert result["headline"] == "Test Headline"
        assert result["category"] == "Tech"
        assert result["impact_score"] == 8
        assert "is_developing" in result


class TestResearchCardSpecifics:
    """Tests for ResearchCard specific attributes."""

    def test_get_card_type_returns_research(self):
        """Test get_card_type returns 'research'."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(topic="Test", source=source, user_id="user1")
        assert card.get_card_type() == "research"

    def test_default_research_depth(self):
        """Test default research_depth is 'quick'."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(topic="Test", source=source, user_id="user1")
        assert card.research_depth == "quick"

    def test_custom_research_depth(self):
        """Test setting custom research_depth."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(
            topic="Test",
            source=source,
            user_id="user1",
            research_depth="detailed",
        )
        assert card.research_depth == "detailed"

    def test_default_key_findings_empty(self):
        """Test default key_findings is empty list."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(topic="Test", source=source, user_id="user1")
        assert card.key_findings == []

    def test_default_sources_count(self):
        """Test default sources_count is 0."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(topic="Test", source=source, user_id="user1")
        assert card.sources_count == 0

    def test_to_dict_includes_research_fields(self):
        """Test to_dict includes research-specific fields."""
        from local_deep_research.news.core.base_card import (
            ResearchCard,
            CardSource,
        )

        source = CardSource(type="user_search")
        card = ResearchCard(
            topic="Test",
            source=source,
            user_id="user1",
            research_depth="detailed",
            sources_count=15,
        )
        result = card.to_dict()

        assert result["research_depth"] == "detailed"
        assert result["sources_count"] == 15
        assert "key_findings" in result


class TestUpdateCardSpecifics:
    """Tests for UpdateCard specific attributes."""

    def test_get_card_type_returns_update(self):
        """Test get_card_type returns 'update'."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        card = UpdateCard(topic="Test", source=source, user_id="user1")
        assert card.get_card_type() == "update"

    def test_default_update_type(self):
        """Test default update_type is 'new_stories'."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        card = UpdateCard(topic="Test", source=source, user_id="user1")
        assert card.update_type == "new_stories"

    def test_custom_update_type(self):
        """Test setting custom update_type."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        card = UpdateCard(
            topic="Test",
            source=source,
            user_id="user1",
            update_type="breaking",
        )
        assert card.update_type == "breaking"

    def test_default_count(self):
        """Test default count is 0."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        card = UpdateCard(topic="Test", source=source, user_id="user1")
        assert card.count == 0

    def test_since_is_set_on_init(self):
        """Test since timestamp is set on initialization."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        before = datetime.now(timezone.utc)
        card = UpdateCard(topic="Test", source=source, user_id="user1")
        after = datetime.now(timezone.utc)

        assert card.since is not None
        assert before <= card.since <= after

    def test_to_dict_includes_update_fields(self):
        """Test to_dict includes update-specific fields."""
        from local_deep_research.news.core.base_card import (
            UpdateCard,
            CardSource,
        )

        source = CardSource(type="subscription")
        card = UpdateCard(
            topic="Test",
            source=source,
            user_id="user1",
            update_type="follow_up",
            count=5,
        )
        result = card.to_dict()

        assert result["update_type"] == "follow_up"
        assert result["count"] == 5
        assert "since" in result


class TestOverviewCardSpecifics:
    """Tests for OverviewCard specific attributes."""

    def test_get_card_type_returns_overview(self):
        """Test get_card_type returns 'overview'."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(source=source, user_id="user1")
        assert card.get_card_type() == "overview"

    def test_default_topic_is_news_overview(self):
        """Test default topic is 'News Overview'."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(source=source, user_id="user1")
        assert card.topic == "News Overview"

    def test_default_stats_structure(self):
        """Test default stats has expected structure."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(source=source, user_id="user1")

        assert "total_new" in card.stats
        assert "breaking" in card.stats
        assert "relevant" in card.stats
        assert "categories" in card.stats

    def test_default_summary_is_empty(self):
        """Test default summary is empty string."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(source=source, user_id="user1")
        assert card.summary == ""

    def test_default_top_stories_empty(self):
        """Test default top_stories is empty list."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(source=source, user_id="user1")
        assert card.top_stories == []

    def test_to_dict_includes_overview_fields(self):
        """Test to_dict includes overview-specific fields."""
        from local_deep_research.news.core.base_card import (
            OverviewCard,
            CardSource,
        )

        source = CardSource(type="news_research")
        card = OverviewCard(
            source=source,
            user_id="user1",
            summary="Today's summary",
            trend_analysis="Trending up",
        )
        result = card.to_dict()

        assert result["summary"] == "Today's summary"
        assert result["trend_analysis"] == "Trending up"
        assert "stats" in result
        assert "top_stories" in result


class TestBaseCardCommonBehavior:
    """Tests for common BaseCard behavior across types."""

    def test_generates_id_when_not_provided(self):
        """Test generates id when card_id not provided."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        assert card.id is not None
        assert len(card.id) > 0

    def test_uses_provided_card_id(self):
        """Test uses provided card_id."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(
            topic="Test",
            source=source,
            user_id="user1",
            card_id="custom-id",
        )
        assert card.id == "custom-id"

    def test_sets_created_at(self):
        """Test sets created_at timestamp."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        before = datetime.now(timezone.utc)
        card = NewsCard(topic="Test", source=source, user_id="user1")
        after = datetime.now(timezone.utc)

        assert card.created_at is not None
        assert before <= card.created_at <= after

    def test_sets_updated_at(self):
        """Test sets updated_at timestamp."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        assert card.updated_at is not None

    def test_initializes_interaction_dict(self):
        """Test initializes interaction dictionary."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        assert card.interaction is not None
        assert card.interaction["votes_up"] == 0
        assert card.interaction["votes_down"] == 0
        assert card.interaction["views"] == 0
        assert card.interaction["shares"] == 0

    def test_initializes_empty_versions_list(self):
        """Test initializes empty versions list."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        assert card.versions == []

    def test_to_base_dict_includes_common_fields(self):
        """Test to_base_dict includes common fields."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item", source_id="src-1")
        card = NewsCard(
            topic="Test",
            source=source,
            user_id="user1",
            card_id="card-123",
        )
        result = card.to_base_dict()

        assert result["id"] == "card-123"
        assert result["topic"] == "Test"
        assert result["user_id"] == "user1"
        assert result["source"]["type"] == "news_item"
        assert result["card_type"] == "news"


class TestBaseCardMethods:
    """Tests for BaseCard methods."""

    def test_set_progress_callback(self):
        """Test set_progress_callback method."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        callback = Mock()
        card.set_progress_callback(callback)

        assert card.progress_callback == callback

    def test_update_progress_calls_callback(self):
        """Test _update_progress calls callback when set."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        callback = Mock()
        card.set_progress_callback(callback)

        card._update_progress("Processing", 50, {"step": 1})

        callback.assert_called_once_with("Processing", 50, {"step": 1})

    def test_update_progress_does_nothing_without_callback(self):
        """Test _update_progress does nothing when no callback."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        # Should not raise
        card._update_progress("Processing", 50)

    def test_get_latest_version_returns_none_when_empty(self):
        """Test get_latest_version returns None when no versions."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card.get_latest_version()
        assert result is None


class TestExtractMethods:
    """Tests for extraction helper methods."""

    def test_extract_headline_from_headline_field(self):
        """Test _extract_headline uses headline field."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_headline({"headline": "Main Headline"})
        assert result == "Main Headline"

    def test_extract_headline_from_title_field(self):
        """Test _extract_headline uses title field as fallback."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_headline({"title": "Title Text"})
        assert result == "Title Text"

    def test_extract_headline_from_query_field(self):
        """Test _extract_headline uses query field as fallback."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_headline({"query": "Search query text"})
        assert result == "Search query text"

    def test_extract_summary_from_summary_field(self):
        """Test _extract_summary uses summary field."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_summary({"summary": "Summary text"})
        assert result == "Summary text"

    def test_calculate_impact_basic(self):
        """Test _calculate_impact basic calculation."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._calculate_impact(
            {
                "findings": [{}] * 10,
                "sources": [{}] * 9,
            }
        )
        # 5 + 10//5 + 9//3 = 5 + 2 + 3 = 10
        assert result == 10

    def test_calculate_impact_minimum_is_one(self):
        """Test _calculate_impact minimum is 1."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._calculate_impact({})
        assert result >= 1

    def test_calculate_impact_maximum_is_ten(self):
        """Test _calculate_impact maximum is 10."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._calculate_impact(
            {
                "findings": [{}] * 100,
                "sources": [{}] * 100,
            }
        )
        assert result <= 10

    def test_extract_topics_from_result(self):
        """Test _extract_topics extracts from result."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_topics({"topics": ["AI", "ML", "Python"]})
        assert result == ["AI", "ML", "Python"]

    def test_extract_topics_from_query(self):
        """Test _extract_topics extracts from query when no topics."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_topics(
            {"query": "artificial intelligence machine learning"}
        )
        # Should extract words > 4 chars
        assert "artificial" in result
        assert "intelligence" in result
        assert "machine" in result
        assert "learning" in result

    def test_extract_entities_default(self):
        """Test _extract_entities returns default structure."""
        from local_deep_research.news.core.base_card import NewsCard, CardSource

        source = CardSource(type="news_item")
        card = NewsCard(topic="Test", source=source, user_id="user1")

        result = card._extract_entities({})
        assert "people" in result
        assert "places" in result
        assert "organizations" in result
