"""
Deep behavioral tests for CardFactory.
Tests card type registry, _reconstruct_card logic, and card creation patterns.
"""

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

import pytest

from local_deep_research.news.core.base_card import (
    NewsCard,
    ResearchCard,
    UpdateCard,
    OverviewCard,
)
from local_deep_research.news.core.card_factory import CardFactory


# --- Card type registry ---


class TestCardTypeRegistry:
    """Tests for the card type registration system."""

    def test_default_types_registered(self):
        assert "news" in CardFactory._card_types
        assert "research" in CardFactory._card_types
        assert "update" in CardFactory._card_types
        assert "overview" in CardFactory._card_types

    def test_news_maps_to_newscard(self):
        assert CardFactory._card_types["news"] is NewsCard

    def test_research_maps_to_researchcard(self):
        assert CardFactory._card_types["research"] is ResearchCard

    def test_update_maps_to_updatecard(self):
        assert CardFactory._card_types["update"] is UpdateCard

    def test_overview_maps_to_overviewcard(self):
        assert CardFactory._card_types["overview"] is OverviewCard

    def test_register_custom_type(self):
        class CustomCard(NewsCard):
            pass

        # Store original and restore after test
        original = CardFactory._card_types.copy()
        try:
            CardFactory.register_card_type("custom", CustomCard)
            assert "custom" in CardFactory._card_types
            assert CardFactory._card_types["custom"] is CustomCard
        finally:
            CardFactory._card_types = original

    def test_register_non_basecard_raises(self):
        with pytest.raises(ValueError):
            CardFactory.register_card_type("bad", dict)

    def test_register_overwrites_existing(self):
        original = CardFactory._card_types.copy()
        try:

            class NewNews(NewsCard):
                pass

            CardFactory.register_card_type("news", NewNews)
            assert CardFactory._card_types["news"] is NewNews
        finally:
            CardFactory._card_types = original


# --- _reconstruct_card ---


class TestReconstructCard:
    """Tests for card reconstruction from storage data."""

    def test_basic_reconstruction(self):
        data = {
            "id": "card-123",
            "card_type": "news",
            "topic": "AI Breakthrough",
            "user_id": "user1",
            "source": {
                "type": "news_search",
                "source_id": "s1",
                "created_from": "test",
                "metadata": {},
            },
        }
        card = CardFactory._reconstruct_card(data)
        assert card is not None
        assert card.id == "card-123"
        assert card.topic == "AI Breakthrough"
        assert card.user_id == "user1"

    def test_returns_correct_card_type_news(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert isinstance(card, NewsCard)

    def test_returns_correct_card_type_research(self):
        data = {
            "id": "card-1",
            "card_type": "research",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert isinstance(card, ResearchCard)

    def test_returns_correct_card_type_update(self):
        data = {
            "id": "card-1",
            "card_type": "update",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert isinstance(card, UpdateCard)

    def test_unknown_card_type_returns_none(self):
        data = {
            "id": "card-1",
            "card_type": "unknown_type",
            "topic": "Test",
            "user_id": "u1",
        }
        assert CardFactory._reconstruct_card(data) is None

    def test_handles_enum_card_type(self):
        """Card type could be an enum with a .value attribute."""
        mock_enum = Mock()
        mock_enum.value = "news"
        data = {
            "id": "card-1",
            "card_type": mock_enum,
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card is not None

    def test_reconstructs_source_from_nested_dict(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {
                "type": "news_search",
                "source_id": "search-456",
                "created_from": "Analysis",
                "metadata": {"key": "val"},
            },
        }
        card = CardFactory._reconstruct_card(data)
        assert card.source.type == "news_search"
        assert card.source.source_id == "search-456"
        assert card.source.created_from == "Analysis"

    def test_constructs_source_from_flat_fields(self):
        """When source is empty, construct from flat fields."""
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {},
            "source_type": "user_search",
            "source_id": "s-789",
            "created_from": "flat test",
        }
        card = CardFactory._reconstruct_card(data)
        assert card.source.type == "user_search"

    def test_constructs_source_when_none(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": None,
        }
        card = CardFactory._reconstruct_card(data)
        assert card.source.type == "unknown"

    def test_handles_iso_string_created_at(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "created_at": "2025-06-15T12:00:00+00:00",
        }
        card = CardFactory._reconstruct_card(data)
        assert card.created_at.year == 2025
        assert card.created_at.month == 6

    def test_handles_z_suffix_in_datetime(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "created_at": "2025-06-15T12:00:00Z",
        }
        card = CardFactory._reconstruct_card(data)
        assert card.created_at is not None

    def test_handles_datetime_object_created_at(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "created_at": dt,
        }
        card = CardFactory._reconstruct_card(data)
        assert card.created_at == dt

    def test_handles_iso_string_updated_at(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "updated_at": "2025-06-16T00:00:00+00:00",
        }
        card = CardFactory._reconstruct_card(data)
        assert card.updated_at is not None

    def test_restores_versions(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "versions": [{"query": "test", "strategy": "s1"}],
        }
        card = CardFactory._reconstruct_card(data)
        assert card.versions == [{"query": "test", "strategy": "s1"}]

    def test_restores_metadata(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "metadata": {"key": "value"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.metadata == {"key": "value"}

    def test_restores_interaction(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
            "interaction": {"views": 5, "voted": "up"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.interaction == {"views": 5, "voted": "up"}

    def test_defaults_user_id_to_unknown(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": None,
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.user_id == "unknown"

    def test_uses_title_when_topic_missing(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "title": "Fallback Title",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.topic == "Fallback Title"

    def test_defaults_missing_card_type_to_news(self):
        data = {
            "id": "card-1",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert isinstance(card, NewsCard)

    def test_returns_none_on_exception(self):
        """Malformed data that causes exception returns None."""
        data = {"bad": "data"}  # Missing 'id' key
        card = CardFactory._reconstruct_card(data)
        assert card is None

    def test_empty_versions_default(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.versions == []

    def test_empty_metadata_default(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.metadata == {}

    def test_empty_interaction_default(self):
        data = {
            "id": "card-1",
            "card_type": "news",
            "topic": "Test",
            "user_id": "u1",
            "source": {"type": "test"},
        }
        card = CardFactory._reconstruct_card(data)
        assert card.interaction == {}
