"""
Deep behavioral tests for card storage field mapping patterns.
Tests _card_to_dict field mapping, source extraction, create field mapping,
update field mapping, and get_by_user filtering.
"""

from datetime import datetime, timezone


# --- _card_to_dict field mapping ---


class TestCardToDictFieldMapping:
    """Tests for the _card_to_dict field mapping pattern from card_storage.py:226-279."""

    def _card_to_dict(self, card_attrs, extra_data=None):
        """Reproduce the mapping without requiring a real ORM object."""
        if extra_data is None:
            extra_data = {}
        return {
            "id": card_attrs.get("id"),
            "topic": card_attrs.get("title"),  # title → topic
            "title": card_attrs.get("title"),
            "summary": card_attrs.get("summary"),
            "content": card_attrs.get("content"),
            "url": card_attrs.get("url"),
            "source_name": card_attrs.get("source_name"),
            "source_type": card_attrs.get("source_type"),
            "source_id": card_attrs.get("source_id"),
            "category": card_attrs.get("category"),
            "tags": card_attrs.get("tags"),
            "card_type": card_attrs.get("card_type"),
            "published_at": card_attrs["published_at"].isoformat()
            if card_attrs.get("published_at")
            else None,
            "discovered_at": card_attrs["discovered_at"].isoformat()
            if card_attrs.get("discovered_at")
            else None,
            "created_at": card_attrs["discovered_at"].isoformat()
            if card_attrs.get("discovered_at")
            else None,
            "updated_at": card_attrs["discovered_at"].isoformat()
            if card_attrs.get("discovered_at")
            else None,
            "is_read": card_attrs.get("is_read"),
            "read_at": card_attrs["read_at"].isoformat()
            if card_attrs.get("read_at")
            else None,
            "is_saved": card_attrs.get("is_saved"),
            "is_pinned": card_attrs.get("is_saved"),  # Alias
            "saved_at": card_attrs["saved_at"].isoformat()
            if card_attrs.get("saved_at")
            else None,
            # Fields from extra_data
            "user_id": extra_data.get("user_id"),
            "parent_card_id": extra_data.get("parent_card_id"),
            "created_from": extra_data.get("created_from"),
            "is_archived": extra_data.get("is_archived", False),
            "metadata": extra_data.get("metadata", {}),
            "interaction": extra_data.get("interaction", {}),
            "source": {
                "type": card_attrs.get("source_type"),
                "source_id": card_attrs.get("source_id"),
                "created_from": extra_data.get("created_from", ""),
                "metadata": extra_data.get("metadata", {}),
            },
        }

    def test_title_mapped_to_topic(self):
        result = self._card_to_dict({"title": "AI News"})
        assert result["topic"] == "AI News"

    def test_title_preserved(self):
        result = self._card_to_dict({"title": "AI News"})
        assert result["title"] == "AI News"

    def test_is_saved_mapped_to_is_pinned(self):
        result = self._card_to_dict({"is_saved": True})
        assert result["is_pinned"] is True

    def test_is_saved_false_maps_pinned_false(self):
        result = self._card_to_dict({"is_saved": False})
        assert result["is_pinned"] is False

    def test_discovered_at_maps_to_created_at(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        result = self._card_to_dict({"discovered_at": dt})
        assert result["created_at"] == dt.isoformat()

    def test_discovered_at_maps_to_updated_at(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        result = self._card_to_dict({"discovered_at": dt})
        assert result["updated_at"] == dt.isoformat()

    def test_none_published_at(self):
        result = self._card_to_dict({"published_at": None})
        assert result["published_at"] is None

    def test_none_discovered_at(self):
        result = self._card_to_dict({"discovered_at": None})
        assert result["created_at"] is None
        assert result["updated_at"] is None

    def test_none_read_at(self):
        result = self._card_to_dict({"read_at": None})
        assert result["read_at"] is None

    def test_none_saved_at(self):
        result = self._card_to_dict({"saved_at": None})
        assert result["saved_at"] is None

    def test_extra_data_user_id(self):
        result = self._card_to_dict({}, extra_data={"user_id": "u1"})
        assert result["user_id"] == "u1"

    def test_extra_data_parent_card_id(self):
        result = self._card_to_dict({}, extra_data={"parent_card_id": "p1"})
        assert result["parent_card_id"] == "p1"

    def test_extra_data_created_from(self):
        result = self._card_to_dict({}, extra_data={"created_from": "Analysis"})
        assert result["created_from"] == "Analysis"

    def test_default_is_archived_false(self):
        result = self._card_to_dict({}, extra_data={})
        assert result["is_archived"] is False

    def test_is_archived_true(self):
        result = self._card_to_dict({}, extra_data={"is_archived": True})
        assert result["is_archived"] is True

    def test_default_metadata_empty(self):
        result = self._card_to_dict({}, extra_data={})
        assert result["metadata"] == {}

    def test_default_interaction_empty(self):
        result = self._card_to_dict({}, extra_data={})
        assert result["interaction"] == {}

    def test_source_dict_constructed(self):
        result = self._card_to_dict(
            {"source_type": "news_search", "source_id": "s1"},
            extra_data={"created_from": "Analysis", "metadata": {}},
        )
        assert result["source"]["type"] == "news_search"
        assert result["source"]["source_id"] == "s1"
        assert result["source"]["created_from"] == "Analysis"


# --- Create field mapping ---


class TestCreateFieldMapping:
    """Tests for the create method's field mapping from card_storage.py:42-98."""

    def _extract_source_info(self, data):
        """Reproduce source extraction from create()."""
        source_info = data.get("source", {})
        if isinstance(source_info, dict):
            source_type = source_info.get("type")
            source_id = source_info.get("source_id")
            created_from = source_info.get("created_from")
        else:
            source_type = data.get("source_type")
            source_id = data.get("source_id")
            created_from = data.get("created_from")
        return source_type, source_id, created_from

    def test_nested_source_dict(self):
        data = {
            "source": {
                "type": "news_search",
                "source_id": "s1",
                "created_from": "Analysis",
            }
        }
        source_type, source_id, created_from = self._extract_source_info(data)
        assert source_type == "news_search"
        assert source_id == "s1"
        assert created_from == "Analysis"

    def test_flat_source_fields(self):
        data = {
            "source": "not_a_dict",
            "source_type": "user_search",
            "source_id": "s2",
            "created_from": "Manual",
        }
        source_type, source_id, created_from = self._extract_source_info(data)
        assert source_type == "user_search"
        assert source_id == "s2"
        assert created_from == "Manual"

    def test_missing_source(self):
        data = {}
        source_type, source_id, created_from = self._extract_source_info(data)
        assert source_type is None
        assert source_id is None
        assert created_from is None

    def test_empty_source_dict(self):
        data = {"source": {}}
        source_type, source_id, created_from = self._extract_source_info(data)
        assert source_type is None

    def test_card_type_resolution(self):
        """card_type falls back to 'type' then 'news'."""
        assert {"card_type": "research"}.get(
            "card_type", {"type": "news"}.get("type", "news")
        ) == "research"

    def test_card_type_fallback_to_type(self):
        data = {"type": "update"}
        card_type = data.get("card_type", data.get("type", "news"))
        assert card_type == "update"

    def test_card_type_default_news(self):
        data = {}
        card_type = data.get("card_type", data.get("type", "news"))
        assert card_type == "news"

    def test_topic_mapped_to_title(self):
        data = {"topic": "My Topic"}
        title = data.get("topic", data.get("title", "Untitled"))
        assert title == "My Topic"

    def test_title_fallback(self):
        data = {"title": "My Title"}
        title = data.get("topic", data.get("title", "Untitled"))
        assert title == "My Title"

    def test_title_default_untitled(self):
        data = {}
        title = data.get("topic", data.get("title", "Untitled"))
        assert title == "Untitled"

    def test_url_from_source_url(self):
        data = {"source_url": "/results/123"}
        url = data.get("url", data.get("source_url"))
        assert url == "/results/123"

    def test_url_preferred_over_source_url(self):
        data = {"url": "https://example.com", "source_url": "/results/123"}
        url = data.get("url", data.get("source_url"))
        assert url == "https://example.com"


# --- Update field mapping ---


class TestUpdateFieldMapping:
    """Tests for the update method's field mapping from card_storage.py:108-141."""

    def test_is_pinned_maps_to_is_saved(self):
        data = {"is_pinned": True}
        is_saved = data.get("is_pinned")
        assert is_saved is True

    def test_pinned_false(self):
        data = {"is_pinned": False}
        is_saved = data.get("is_pinned")
        assert is_saved is False

    def test_last_viewed_sets_is_read(self):
        data = {"last_viewed": datetime.now(timezone.utc)}
        has_last_viewed = "last_viewed" in data
        assert has_last_viewed

    def test_is_archived_stored_in_extra_data(self):
        data = {"is_archived": True}
        extra_data = {}
        if "is_archived" in data:
            extra_data["is_archived"] = data["is_archived"]
        assert extra_data["is_archived"] is True

    def test_interaction_stored_in_extra_data(self):
        data = {"interaction": {"views": 5}}
        extra_data = {}
        if "interaction" in data:
            extra_data["interaction"] = data["interaction"]
        assert extra_data["interaction"]["views"] == 5

    def test_no_fields_no_changes(self):
        data = {}
        extra_data = {}
        if "is_archived" in data:
            extra_data["is_archived"] = data["is_archived"]
        if "interaction" in data:
            extra_data["interaction"] = data["interaction"]
        assert extra_data == {}


# --- get_by_user filtering ---


class TestGetByUserFiltering:
    """Tests for the get_by_user post-filtering from card_storage.py:281-302."""

    def _filter_by_user(self, all_cards, user_id, offset=0, limit=50):
        """Reproduce get_by_user filtering."""
        user_cards = [
            card
            for card in all_cards
            if card.get("user_id") == user_id
            and not card.get("is_archived", False)
        ]
        return user_cards[offset : offset + limit]

    def test_filters_by_user_id(self):
        cards = [
            {"user_id": "u1", "title": "A"},
            {"user_id": "u2", "title": "B"},
            {"user_id": "u1", "title": "C"},
        ]
        result = self._filter_by_user(cards, "u1")
        assert len(result) == 2

    def test_excludes_archived(self):
        cards = [
            {"user_id": "u1", "title": "A", "is_archived": False},
            {"user_id": "u1", "title": "B", "is_archived": True},
        ]
        result = self._filter_by_user(cards, "u1")
        assert len(result) == 1
        assert result[0]["title"] == "A"

    def test_default_is_archived_false(self):
        cards = [{"user_id": "u1", "title": "A"}]
        result = self._filter_by_user(cards, "u1")
        assert len(result) == 1

    def test_pagination_offset(self):
        cards = [{"user_id": "u1", "title": f"Card {i}"} for i in range(10)]
        result = self._filter_by_user(cards, "u1", offset=5)
        assert len(result) == 5

    def test_pagination_limit(self):
        cards = [{"user_id": "u1", "title": f"Card {i}"} for i in range(10)]
        result = self._filter_by_user(cards, "u1", limit=3)
        assert len(result) == 3

    def test_empty_cards(self):
        result = self._filter_by_user([], "u1")
        assert result == []

    def test_no_matching_user(self):
        cards = [{"user_id": "u2", "title": "A"}]
        result = self._filter_by_user(cards, "u1")
        assert result == []


# --- get_latest_version logic ---


class TestGetLatestVersionLogic:
    """Tests for get_latest_version from card_storage.py:304-327."""

    def _get_latest_version(self, card_data):
        """Reproduce get_latest_version logic."""
        if not card_data:
            return None
        extra_data = card_data.get("metadata", {})
        if "latest_version" in extra_data:
            return extra_data["latest_version"]
        card_id = card_data.get("id", "unknown")
        return {
            "version_id": f"{card_id}_v1",
            "version_number": 1,
            "headline": card_data.get("title"),
            "summary": card_data.get("summary"),
            "card_id": card_id,
        }

    def test_none_card_returns_none(self):
        assert self._get_latest_version(None) is None

    def test_with_latest_version(self):
        card = {
            "id": "c1",
            "metadata": {
                "latest_version": {"version_id": "v2", "headline": "Updated"}
            },
        }
        result = self._get_latest_version(card)
        assert result["version_id"] == "v2"

    def test_default_version_1(self):
        card = {"id": "c1", "title": "Test", "summary": "Sum", "metadata": {}}
        result = self._get_latest_version(card)
        assert result["version_number"] == 1

    def test_default_version_id_format(self):
        card = {"id": "card-123", "title": "Test", "metadata": {}}
        result = self._get_latest_version(card)
        assert result["version_id"] == "card-123_v1"

    def test_default_includes_headline(self):
        card = {"id": "c1", "title": "Headline", "metadata": {}}
        result = self._get_latest_version(card)
        assert result["headline"] == "Headline"

    def test_default_includes_summary(self):
        card = {"id": "c1", "title": "T", "summary": "Summary", "metadata": {}}
        result = self._get_latest_version(card)
        assert result["summary"] == "Summary"

    def test_default_includes_card_id(self):
        card = {"id": "c1", "title": "T", "metadata": {}}
        result = self._get_latest_version(card)
        assert result["card_id"] == "c1"

    def test_no_metadata_key(self):
        card = {"id": "c1", "title": "T"}
        result = self._get_latest_version(card)
        assert result["version_number"] == 1
