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

from unittest.mock import Mock

import pytest

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


# --- 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"}
