"""Tests for uncovered paths in local_deep_research.news.core.base_card."""

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

import pytest

from local_deep_research.news.core.base_card import (
    BaseCard,
    CardSource,
    CardVersion,
)


class _TestCard(BaseCard):
    """Concrete subclass for testing abstract BaseCard."""

    def get_card_type(self):
        return "test"

    def to_dict(self):
        return self.to_base_dict()


def _make_card(**overrides):
    defaults = dict(
        topic="test topic",
        source=CardSource(type="test"),
        user_id="user-1",
    )
    defaults.update(overrides)
    return _TestCard(**defaults)


class TestSaveWithoutStorage:
    def test_raises_runtime_error(self):
        card = _make_card()
        with pytest.raises(RuntimeError, match="Storage must be set"):
            card.save()


class TestSaveWithStorage:
    def test_calls_storage_create(self):
        card = _make_card()
        mock_storage = Mock()
        mock_storage.create.return_value = "stored-id"
        card.storage = mock_storage

        result = card.save()

        assert result == "stored-id"
        call_data = mock_storage.create.call_args[0][0]
        assert call_data["id"] == card.id
        assert call_data["user_id"] == "user-1"
        assert call_data["topic"] == "test topic"
        assert call_data["card_type"] == "test"
        assert call_data["source_type"] == "test"


class TestAddVersionWithoutStorage:
    def test_raises_runtime_error(self):
        card = _make_card()
        with pytest.raises(RuntimeError, match="Storage must be set"):
            card.add_version({"findings": []}, "query", "strategy")


class TestAddVersionWithStorage:
    def test_creates_version_and_returns_id(self):
        card = _make_card()
        mock_storage = Mock()
        mock_storage.add_version.return_value = "ver-42"
        card.storage = mock_storage

        result = card.add_version(
            {"findings": ["f1"], "sources": ["s1"]}, "my query", "quick"
        )

        assert result == "ver-42"
        assert len(card.versions) == 1
        assert card.versions[0].version_id == "ver-42"
        assert card.versions[0].query_used == "my query"
        assert card.versions[0].search_strategy == "quick"


class TestGetLatestVersion:
    def test_no_versions_returns_none(self):
        card = _make_card()
        assert card.get_latest_version() is None

    def test_returns_latest_by_created_at(self):
        card = _make_card()
        now = datetime.now(tz=timezone.utc)
        v_old = CardVersion(
            version_id="old",
            created_at=now - timedelta(hours=2),
            content={},
            query_used="q1",
        )
        v_new = CardVersion(
            version_id="new",
            created_at=now,
            content={},
            query_used="q2",
        )
        v_mid = CardVersion(
            version_id="mid",
            created_at=now - timedelta(hours=1),
            content={},
            query_used="q3",
        )
        card.versions = [v_old, v_new, v_mid]

        latest = card.get_latest_version()
        assert latest.version_id == "new"


class TestExtractHeadline:
    def test_uses_headline_field(self):
        card = _make_card()
        assert card._extract_headline({"headline": "Big News"}) == "Big News"

    def test_falls_back_to_title(self):
        card = _make_card()
        assert card._extract_headline({"title": "Title Here"}) == "Title Here"

    def test_falls_back_to_query_truncated(self):
        card = _make_card()
        long_query = "x" * 200
        result = card._extract_headline({"query": long_query})
        assert result == long_query[:100]

    def test_empty_result_returns_empty_string(self):
        card = _make_card()
        assert card._extract_headline({}) == ""


class TestExtractSummary:
    def test_uses_summary_field(self):
        card = _make_card()
        assert card._extract_summary({"summary": "Sum"}) == "Sum"

    def test_falls_back_to_current_knowledge(self):
        card = _make_card()
        result = card._extract_summary({"current_knowledge": "CK"})
        assert result == "CK"

    def test_falls_back_to_formatted_findings_truncated(self):
        card = _make_card()
        long_text = "y" * 600
        result = card._extract_summary({"formatted_findings": long_text})
        assert result == long_text[:500]

    def test_empty_result_returns_empty_string(self):
        card = _make_card()
        assert card._extract_summary({}) == ""


class TestCalculateImpact:
    def test_no_findings_no_sources(self):
        card = _make_card()
        score = card._calculate_impact({})
        # 5 + 0 + 0 = 5, clamped [1, 10]
        assert score == 5

    def test_many_findings_and_sources_capped_at_10(self):
        card = _make_card()
        result = {
            "findings": list(range(100)),
            "sources": list(range(100)),
        }
        score = card._calculate_impact(result)
        assert score == 10

    def test_some_findings_and_sources(self):
        card = _make_card()
        # 5 findings -> 5//5=1, 6 sources -> 6//3=2  => 5+1+2=8
        result = {"findings": [1, 2, 3, 4, 5], "sources": [1, 2, 3, 4, 5, 6]}
        score = card._calculate_impact(result)
        assert score == 8


class TestExtractTopics:
    def test_uses_topics_from_result(self):
        card = _make_card()
        topics = card._extract_topics({"topics": ["ai", "ml"]})
        assert topics == ["ai", "ml"]

    def test_extracts_words_from_query_when_no_topics(self):
        card = _make_card()
        topics = card._extract_topics(
            {"query": "large language models artificial intelligence"}
        )
        # words > 4 chars: large, language, models, artificial, intelligence
        assert "large" in topics
        assert "language" in topics
        assert len(topics) <= 5

    def test_empty_result_returns_empty_list(self):
        card = _make_card()
        assert card._extract_topics({}) == []


class TestExtractEntities:
    def test_uses_entities_from_result(self):
        card = _make_card()
        entities = {"people": ["Alice"], "places": [], "organizations": []}
        result = card._extract_entities({"entities": entities})
        assert result == entities

    def test_returns_default_structure_when_missing(self):
        card = _make_card()
        result = card._extract_entities({})
        assert result == {"people": [], "places": [], "organizations": []}


class TestToBaseDict:
    def test_includes_all_expected_fields(self):
        card = _make_card(parent_card_id="parent-1", metadata={"k": "v"})
        d = card.to_base_dict()

        assert d["id"] == card.id
        assert d["topic"] == "test topic"
        assert d["user_id"] == "user-1"
        assert d["created_at"] is not None
        assert d["updated_at"] is not None
        assert d["source"]["type"] == "test"
        assert d["versions_count"] == 0
        assert d["parent_card_id"] == "parent-1"
        assert d["metadata"] == {"k": "v"}
        assert d["interaction"]["votes_up"] == 0
        assert d["card_type"] == "test"


class TestCardVersionPostInit:
    def test_empty_version_id_generates_one(self):
        v = CardVersion(
            version_id="",
            created_at=datetime.now(tz=timezone.utc),
            content={},
            query_used="q",
        )
        assert v.version_id != ""
        assert len(v.version_id) > 0

    def test_provided_version_id_kept(self):
        v = CardVersion(
            version_id="my-id",
            created_at=datetime.now(tz=timezone.utc),
            content={},
            query_used="q",
        )
        assert v.version_id == "my-id"
