"""
Deep behavioral tests for BaseRatingSystem, QualityRatingSystem,
RelevanceRatingSystem enums, and SearchSubscriptionFactory.
Tests rating validation, record creation, enum values, and factory methods.
"""

from unittest.mock import Mock, MagicMock, patch

import pytest

from local_deep_research.news.rating_system.base_rater import (
    BaseRatingSystem,
    QualityRating,
    QualityRatingSystem,
    RelevanceRating,
    RelevanceRatingSystem,
)


# Mock SQLSubscriptionStorage before importing subscription classes
with patch(
    "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage",
    return_value=MagicMock(),
):
    from local_deep_research.news.subscription_manager.search_subscription import (
        SearchSubscription,
        SearchSubscriptionFactory,
    )

_storage_patch = patch(
    "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage",
    return_value=MagicMock(),
)
_storage_patch.start()


# --- RelevanceRating enum ---


class TestRelevanceRatingEnum:
    """Tests for RelevanceRating enum values."""

    def test_up_value(self):
        assert RelevanceRating.UP.value == "up"

    def test_down_value(self):
        assert RelevanceRating.DOWN.value == "down"

    def test_only_two_values(self):
        assert len(RelevanceRating) == 2

    def test_from_string_up(self):
        assert RelevanceRating("up") == RelevanceRating.UP

    def test_from_string_down(self):
        assert RelevanceRating("down") == RelevanceRating.DOWN

    def test_invalid_value_raises(self):
        with pytest.raises(ValueError):
            RelevanceRating("neutral")


# --- QualityRating enum ---


class TestQualityRatingEnum:
    """Tests for QualityRating enum values."""

    def test_one_star(self):
        assert QualityRating.ONE_STAR.value == 1

    def test_two_stars(self):
        assert QualityRating.TWO_STARS.value == 2

    def test_three_stars(self):
        assert QualityRating.THREE_STARS.value == 3

    def test_four_stars(self):
        assert QualityRating.FOUR_STARS.value == 4

    def test_five_stars(self):
        assert QualityRating.FIVE_STARS.value == 5

    def test_five_values(self):
        assert len(QualityRating) == 5

    def test_from_int(self):
        assert QualityRating(3) == QualityRating.THREE_STARS

    def test_invalid_int_raises(self):
        with pytest.raises(ValueError):
            QualityRating(6)

    def test_zero_raises(self):
        with pytest.raises(ValueError):
            QualityRating(0)


# --- BaseRatingSystem abstract ---


class TestBaseRatingSystemAbstract:
    """Tests for BaseRatingSystem abstract enforcement."""

    def test_cannot_instantiate_directly(self):
        with pytest.raises(TypeError):
            BaseRatingSystem()

    def test_storage_backend_default_none(self):
        rs = QualityRatingSystem()
        assert rs.storage_backend is None

    def test_storage_backend_injected(self):
        backend = Mock()
        rs = QualityRatingSystem(storage_backend=backend)
        assert rs.storage_backend is backend

    def test_rating_type_is_class_name(self):
        rs = QualityRatingSystem()
        assert rs.rating_type == "QualityRatingSystem"

    def test_relevance_rating_type(self):
        rs = RelevanceRatingSystem()
        assert rs.rating_type == "RelevanceRatingSystem"


# --- QualityRatingSystem ---


class TestQualityRatingSystem:
    """Tests for QualityRatingSystem behavior."""

    def test_get_rating_type(self):
        rs = QualityRatingSystem()
        assert rs.get_rating_type() == "quality"

    def test_rate_returns_success(self):
        rs = QualityRatingSystem()
        result = rs.rate("u1", "card1", QualityRating.FIVE_STARS)
        assert result["success"] is True

    def test_rate_returns_message(self):
        rs = QualityRatingSystem()
        result = rs.rate("u1", "card1", QualityRating.THREE_STARS)
        assert "3 stars" in result["message"]

    def test_rate_returns_record(self):
        rs = QualityRatingSystem()
        result = rs.rate("u1", "card1", QualityRating.FOUR_STARS)
        record = result["rating"]
        assert record["user_id"] == "u1"
        assert record["card_id"] == "card1"

    def test_rate_record_has_type(self):
        rs = QualityRatingSystem()
        result = rs.rate("u1", "card1", QualityRating.ONE_STAR)
        assert result["rating"]["rating_type"] == "quality"

    def test_rate_record_has_timestamp(self):
        rs = QualityRatingSystem()
        result = rs.rate("u1", "card1", QualityRating.TWO_STARS)
        assert "rated_at" in result["rating"]

    def test_validate_rejects_none(self):
        rs = QualityRatingSystem()
        with pytest.raises(ValueError, match="cannot be None"):
            rs.rate("u1", "card1", None)

    def test_validate_rejects_wrong_type(self):
        rs = QualityRatingSystem()
        with pytest.raises(ValueError, match="QualityRating"):
            rs.rate("u1", "card1", 5)

    def test_validate_rejects_relevance_enum(self):
        rs = QualityRatingSystem()
        with pytest.raises(ValueError, match="QualityRating"):
            rs.rate("u1", "card1", RelevanceRating.UP)

    def test_get_rating_default_none(self):
        rs = QualityRatingSystem()
        assert rs.get_rating("u1", "card1") is None

    def test_rate_with_metadata(self):
        rs = QualityRatingSystem()
        result = rs.rate(
            "u1", "card1", QualityRating.FIVE_STARS, metadata={"source": "ui"}
        )
        assert result["rating"]["metadata"] == {"source": "ui"}


# --- RelevanceRatingSystem ---


class TestRelevanceRatingSystem:
    """Tests for RelevanceRatingSystem behavior."""

    def test_get_rating_type(self):
        rs = RelevanceRatingSystem()
        assert rs.get_rating_type() == "relevance"

    def test_rate_up(self):
        rs = RelevanceRatingSystem()
        result = rs.rate("u1", "card1", RelevanceRating.UP)
        assert result["success"] is True
        assert "up" in result["message"]

    def test_rate_down(self):
        rs = RelevanceRatingSystem()
        result = rs.rate("u1", "card1", RelevanceRating.DOWN)
        assert result["success"] is True
        assert "down" in result["message"]

    def test_validate_rejects_none(self):
        rs = RelevanceRatingSystem()
        with pytest.raises(ValueError, match="cannot be None"):
            rs.rate("u1", "card1", None)

    def test_validate_rejects_string(self):
        rs = RelevanceRatingSystem()
        with pytest.raises(ValueError, match="RelevanceRating"):
            rs.rate("u1", "card1", "up")

    def test_validate_rejects_quality_enum(self):
        rs = RelevanceRatingSystem()
        with pytest.raises(ValueError, match="RelevanceRating"):
            rs.rate("u1", "card1", QualityRating.FIVE_STARS)

    def test_get_rating_default_none(self):
        rs = RelevanceRatingSystem()
        assert rs.get_rating("u1", "card1") is None


# --- Default method implementations ---


class TestDefaultMethodImplementations:
    """Tests for default base class method implementations."""

    def test_get_recent_ratings_returns_empty(self):
        rs = QualityRatingSystem()
        assert rs.get_recent_ratings("u1") == []

    def test_get_recent_ratings_with_limit(self):
        rs = QualityRatingSystem()
        assert rs.get_recent_ratings("u1", limit=10) == []

    def test_get_card_ratings_returns_default(self):
        rs = QualityRatingSystem()
        result = rs.get_card_ratings("card1")
        assert result["total"] == 0
        assert result["average"] is None

    def test_remove_rating_returns_false(self):
        rs = QualityRatingSystem()
        assert rs.remove_rating("u1", "card1") is False


# --- _create_rating_record ---


class TestCreateRatingRecord:
    """Tests for _create_rating_record helper."""

    def test_has_user_id(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS
        )
        assert record["user_id"] == "u1"

    def test_has_card_id(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS
        )
        assert record["card_id"] == "card1"

    def test_has_rating_type(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS
        )
        assert record["rating_type"] == "quality"

    def test_has_rated_at(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS
        )
        assert "rated_at" in record

    def test_metadata_default_empty(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS
        )
        assert record["metadata"] == {}

    def test_metadata_passed_through(self):
        rs = QualityRatingSystem()
        record = rs._create_rating_record(
            "u1", "card1", QualityRating.FIVE_STARS, metadata={"key": "val"}
        )
        assert record["metadata"] == {"key": "val"}


# --- SearchSubscriptionFactory ---


class TestSearchSubscriptionFactoryFromSearch:
    """Tests for SearchSubscriptionFactory.from_user_search."""

    def test_creates_subscription(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "AI news")
        assert isinstance(sub, SearchSubscription)

    def test_stores_user_id(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "AI news")
        assert sub.user_id == "u1"

    def test_stores_query(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "AI news")
        assert sub.query == "AI news"

    def test_source_type_user_search(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "test")
        assert sub.source.type == "user_search"

    def test_source_has_search_result_id(self):
        sub = SearchSubscriptionFactory.from_user_search(
            "u1", "test", search_result_id="res-123"
        )
        assert sub.source.source_id == "res-123"

    def test_source_created_from_has_query(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "AI news")
        assert "AI news" in sub.source.created_from

    def test_none_search_result_id(self):
        sub = SearchSubscriptionFactory.from_user_search("u1", "test")
        assert sub.source.source_id is None


class TestSearchSubscriptionFactoryFromRecommendation:
    """Tests for SearchSubscriptionFactory.from_recommendation."""

    def test_creates_subscription(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "AI trends", "user history"
        )
        assert isinstance(sub, SearchSubscription)

    def test_source_type_recommendation(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "AI trends", "user history"
        )
        assert sub.source.type == "recommendation"

    def test_source_has_recommendation_source(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "AI trends", "topic analysis"
        )
        assert "topic analysis" in sub.source.created_from

    def test_default_recommendation_type(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "test", "source"
        )
        assert sub.source.metadata["recommendation_type"] == "topic_based"

    def test_metadata_has_recommendation_type(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "test", "source"
        )
        assert "recommendation_type" in sub.source.metadata

    def test_stores_query(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "climate news", "interests"
        )
        assert sub.query == "climate news"

    def test_stores_user_id(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "test", "source"
        )
        assert sub.user_id == "u1"

    def test_is_active_by_default(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "test", "source"
        )
        assert sub.is_active is True

    def test_subscription_type_search(self):
        sub = SearchSubscriptionFactory.from_recommendation(
            "u1", "test", "source"
        )
        assert sub.subscription_type == "search"
