"""
Tests for news/rating_system/base_rater.py

Tests cover:
- RelevanceRating and QualityRating enums
- BaseRatingSystem abstract class
- QualityRatingSystem implementation
- RelevanceRatingSystem implementation
- Rating validation
- Rating record creation
- Default method implementations
"""

import pytest
from unittest.mock import Mock
from datetime import datetime


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

    def test_relevance_rating_up_value(self):
        """RelevanceRating.UP has correct value."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRating,
        )

        assert RelevanceRating.UP.value == "up"

    def test_relevance_rating_down_value(self):
        """RelevanceRating.DOWN has correct value."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRating,
        )

        assert RelevanceRating.DOWN.value == "down"

    def test_relevance_rating_has_two_values(self):
        """RelevanceRating has exactly two values."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRating,
        )

        assert len(RelevanceRating) == 2


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

    def test_quality_rating_one_star_value(self):
        """QualityRating.ONE_STAR has value 1."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRating,
        )

        assert QualityRating.ONE_STAR.value == 1

    def test_quality_rating_five_stars_value(self):
        """QualityRating.FIVE_STARS has value 5."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRating,
        )

        assert QualityRating.FIVE_STARS.value == 5

    def test_quality_rating_has_five_values(self):
        """QualityRating has exactly five values."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRating,
        )

        assert len(QualityRating) == 5

    def test_quality_rating_all_values(self):
        """QualityRating has values 1 through 5."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRating,
        )

        values = [r.value for r in QualityRating]
        assert values == [1, 2, 3, 4, 5]


class TestBaseRatingSystemInit:
    """Tests for BaseRatingSystem initialization."""

    def test_base_rating_system_is_abstract(self):
        """BaseRatingSystem is an abstract class."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )
        from abc import ABC

        assert issubclass(BaseRatingSystem, ABC)

    def test_cannot_instantiate_base_rating_system(self):
        """Cannot directly instantiate BaseRatingSystem."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        with pytest.raises(TypeError):
            BaseRatingSystem()

    def test_subclass_must_implement_rate(self):
        """Subclass without rate implementation cannot be instantiated."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class IncompleteRater(BaseRatingSystem):
            def get_rating(self, user_id, card_id):
                return None

            def get_rating_type(self):
                return "test"

        with pytest.raises(TypeError):
            IncompleteRater()

    def test_subclass_must_implement_get_rating(self):
        """Subclass without get_rating implementation cannot be instantiated."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class IncompleteRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                return {}

            def get_rating_type(self):
                return "test"

        with pytest.raises(TypeError):
            IncompleteRater()

    def test_subclass_must_implement_get_rating_type(self):
        """Subclass without get_rating_type implementation cannot be instantiated."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class IncompleteRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                return {}

            def get_rating(self, user_id, card_id):
                return None

        with pytest.raises(TypeError):
            IncompleteRater()


class TestConcreteBaseRatingSystem:
    """Tests using a concrete implementation of BaseRatingSystem."""

    @pytest.fixture
    def concrete_rater_class(self):
        """Create a minimal concrete implementation."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class TestRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                return {"success": True}

            def get_rating(self, user_id, card_id):
                return None

            def get_rating_type(self):
                return "test"

        return TestRater

    def test_init_with_storage_backend(self, concrete_rater_class):
        """Initializes with storage backend."""
        mock_storage = Mock()
        rater = concrete_rater_class(storage_backend=mock_storage)

        assert rater.storage_backend is mock_storage

    def test_init_without_storage_backend(self, concrete_rater_class):
        """Initializes without storage backend."""
        rater = concrete_rater_class()

        assert rater.storage_backend is None

    def test_rating_type_is_class_name(self, concrete_rater_class):
        """rating_type property is set to class name."""
        rater = concrete_rater_class()

        assert rater.rating_type == "TestRater"


class TestBaseRatingSystemDefaultMethods:
    """Tests for default method implementations in BaseRatingSystem."""

    @pytest.fixture
    def concrete_rater(self):
        """Create a concrete rater instance."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class TestRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                return {"success": True}

            def get_rating(self, user_id, card_id):
                return None

            def get_rating_type(self):
                return "test"

        return TestRater()

    def test_get_recent_ratings_default(self, concrete_rater):
        """Default get_recent_ratings returns empty list."""
        result = concrete_rater.get_recent_ratings("user123", limit=50)

        assert result == []

    def test_get_card_ratings_default(self, concrete_rater):
        """Default get_card_ratings returns empty aggregation."""
        result = concrete_rater.get_card_ratings("card123")

        assert result == {"total": 0, "average": None}

    def test_remove_rating_default(self, concrete_rater):
        """Default remove_rating returns False."""
        result = concrete_rater.remove_rating("user123", "card123")

        assert result is False


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

    @pytest.fixture
    def concrete_rater(self):
        """Create a concrete rater instance."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class TestRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                return self._create_rating_record(
                    user_id, card_id, rating_value, metadata
                )

            def get_rating(self, user_id, card_id):
                return None

            def get_rating_type(self):
                return "test"

        return TestRater()

    def test_create_rating_record_basic(self, concrete_rater):
        """Creates rating record with basic fields."""
        record = concrete_rater._create_rating_record(
            "user123", "card456", "up"
        )

        assert record["user_id"] == "user123"
        assert record["card_id"] == "card456"
        assert record["rating_type"] == "test"
        assert record["rating_value"] == "up"
        assert "rated_at" in record
        assert record["metadata"] == {}

    def test_create_rating_record_with_metadata(self, concrete_rater):
        """Creates rating record with metadata."""
        metadata = {"source": "web", "version": "1.0"}
        record = concrete_rater._create_rating_record(
            "user123", "card456", "up", metadata
        )

        assert record["metadata"] == metadata

    def test_create_rating_record_has_timestamp(self, concrete_rater):
        """Rating record has ISO format timestamp."""
        record = concrete_rater._create_rating_record(
            "user123", "card456", "up"
        )

        # Should be parseable as ISO format
        rated_at = record["rated_at"]
        assert isinstance(rated_at, str)
        # Should not raise
        datetime.fromisoformat(rated_at.replace("Z", "+00:00"))


class TestValidateRatingValue:
    """Tests for _validate_rating_value method."""

    @pytest.fixture
    def concrete_rater(self):
        """Create a concrete rater instance."""
        from local_deep_research.news.rating_system.base_rater import (
            BaseRatingSystem,
        )

        class TestRater(BaseRatingSystem):
            def rate(self, user_id, card_id, rating_value, metadata=None):
                self._validate_rating_value(rating_value)
                return {"success": True}

            def get_rating(self, user_id, card_id):
                return None

            def get_rating_type(self):
                return "test"

        return TestRater()

    def test_validate_rating_value_none_raises(self, concrete_rater):
        """None rating value raises ValueError."""
        with pytest.raises(ValueError, match="cannot be None"):
            concrete_rater._validate_rating_value(None)

    def test_validate_rating_value_accepts_string(self, concrete_rater):
        """String rating value is accepted."""
        # Should not raise
        concrete_rater._validate_rating_value("up")

    def test_validate_rating_value_accepts_number(self, concrete_rater):
        """Number rating value is accepted."""
        # Should not raise
        concrete_rater._validate_rating_value(5)


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

    def test_get_rating_type(self):
        """get_rating_type returns 'quality'."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()

        assert rater.get_rating_type() == "quality"

    def test_rate_with_valid_quality_rating(self):
        """rate accepts valid QualityRating enum."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        result = rater.rate("user123", "card456", QualityRating.FOUR_STARS)

        assert result["success"] is True
        assert "rating" in result
        assert "4 stars" in result["message"]

    def test_rate_with_invalid_type_raises(self):
        """rate raises ValueError for non-QualityRating value."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()

        with pytest.raises(ValueError, match="QualityRating enum"):
            rater.rate("user123", "card456", 4)  # Integer instead of enum

    def test_rate_with_string_raises(self):
        """rate raises ValueError for string value."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()

        with pytest.raises(ValueError, match="QualityRating enum"):
            rater.rate("user123", "card456", "FOUR_STARS")

    def test_rate_with_none_raises(self):
        """rate raises ValueError for None value."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()

        with pytest.raises(ValueError, match="cannot be None"):
            rater.rate("user123", "card456", None)

    def test_rate_with_metadata(self):
        """rate accepts metadata."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        metadata = {"source": "mobile_app"}
        result = rater.rate(
            "user123", "card456", QualityRating.FIVE_STARS, metadata
        )

        assert result["rating"]["metadata"] == metadata

    def test_rate_creates_record_with_correct_type(self):
        """rate creates record with rating_type 'quality'."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        result = rater.rate("user123", "card456", QualityRating.THREE_STARS)

        assert result["rating"]["rating_type"] == "quality"

    def test_rate_with_storage_backend(self):
        """rate works with storage backend (currently no-op)."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        mock_storage = Mock()
        rater = QualityRatingSystem(storage_backend=mock_storage)
        result = rater.rate("user123", "card456", QualityRating.FOUR_STARS)

        # Should succeed (storage implementation is placeholder)
        assert result["success"] is True

    def test_get_rating_returns_none_without_storage(self):
        """get_rating returns None without storage backend."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()
        result = rater.get_rating("user123", "card456")

        assert result is None

    def test_get_rating_with_storage_backend(self):
        """get_rating works with storage backend (currently returns None)."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        mock_storage = Mock()
        rater = QualityRatingSystem(storage_backend=mock_storage)
        result = rater.get_rating("user123", "card456")

        # Current implementation returns None even with storage
        assert result is None


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

    def test_get_rating_type(self):
        """get_rating_type returns 'relevance'."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
        )

        rater = RelevanceRatingSystem()

        assert rater.get_rating_type() == "relevance"

    def test_rate_with_valid_up_rating(self):
        """rate accepts RelevanceRating.UP."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()
        result = rater.rate("user123", "card456", RelevanceRating.UP)

        assert result["success"] is True
        assert "thumbs up" in result["message"]

    def test_rate_with_valid_down_rating(self):
        """rate accepts RelevanceRating.DOWN."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()
        result = rater.rate("user123", "card456", RelevanceRating.DOWN)

        assert result["success"] is True
        assert "thumbs down" in result["message"]

    def test_rate_with_invalid_type_raises(self):
        """rate raises ValueError for non-RelevanceRating value."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
        )

        rater = RelevanceRatingSystem()

        with pytest.raises(ValueError, match="RelevanceRating"):
            rater.rate("user123", "card456", "up")  # String instead of enum

    def test_rate_with_quality_rating_raises(self):
        """rate raises ValueError when given QualityRating."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            QualityRating,
        )

        rater = RelevanceRatingSystem()

        with pytest.raises(ValueError, match="RelevanceRating"):
            rater.rate("user123", "card456", QualityRating.FIVE_STARS)

    def test_rate_with_none_raises(self):
        """rate raises ValueError for None value."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
        )

        rater = RelevanceRatingSystem()

        with pytest.raises(ValueError, match="cannot be None"):
            rater.rate("user123", "card456", None)

    def test_rate_with_metadata(self):
        """rate accepts metadata."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()
        metadata = {"reason": "not_interested"}
        result = rater.rate(
            "user123", "card456", RelevanceRating.DOWN, metadata
        )

        assert result["rating"]["metadata"] == metadata

    def test_rate_creates_record_with_correct_type(self):
        """rate creates record with rating_type 'relevance'."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()
        result = rater.rate("user123", "card456", RelevanceRating.UP)

        assert result["rating"]["rating_type"] == "relevance"

    def test_get_rating_returns_none_without_storage(self):
        """get_rating returns None without storage backend."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
        )

        rater = RelevanceRatingSystem()
        result = rater.get_rating("user123", "card456")

        assert result is None


class TestRatingSystemInheritance:
    """Tests for inheritance relationships."""

    def test_quality_rating_system_inherits_base(self):
        """QualityRatingSystem inherits from BaseRatingSystem."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            BaseRatingSystem,
        )

        assert issubclass(QualityRatingSystem, BaseRatingSystem)

    def test_relevance_rating_system_inherits_base(self):
        """RelevanceRatingSystem inherits from BaseRatingSystem."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            BaseRatingSystem,
        )

        assert issubclass(RelevanceRatingSystem, BaseRatingSystem)


class TestEdgeCases:
    """Tests for edge cases and boundary conditions."""

    def test_empty_user_id(self):
        """Handles empty user_id."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        result = rater.rate("", "card456", QualityRating.THREE_STARS)

        assert result["rating"]["user_id"] == ""

    def test_empty_card_id(self):
        """Handles empty card_id."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        result = rater.rate("user123", "", QualityRating.THREE_STARS)

        assert result["rating"]["card_id"] == ""

    def test_unicode_in_metadata(self):
        """Handles unicode in metadata."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()
        metadata = {"comment": "很好的文章 👍"}
        result = rater.rate("user123", "card456", RelevanceRating.UP, metadata)

        assert result["rating"]["metadata"]["comment"] == "很好的文章 👍"

    def test_large_metadata(self):
        """Handles large metadata dictionary."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()
        metadata = {f"key_{i}": f"value_{i}" for i in range(100)}
        result = rater.rate(
            "user123", "card456", QualityRating.FIVE_STARS, metadata
        )

        assert len(result["rating"]["metadata"]) == 100

    def test_all_quality_ratings(self):
        """Can rate with all quality rating values."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
            QualityRating,
        )

        rater = QualityRatingSystem()

        for rating in QualityRating:
            result = rater.rate("user123", "card456", rating)
            assert result["success"] is True

    def test_both_relevance_ratings(self):
        """Can rate with both relevance rating values."""
        from local_deep_research.news.rating_system.base_rater import (
            RelevanceRatingSystem,
            RelevanceRating,
        )

        rater = RelevanceRatingSystem()

        for rating in RelevanceRating:
            result = rater.rate("user123", "card456", rating)
            assert result["success"] is True

    def test_get_recent_ratings_with_custom_limit(self):
        """get_recent_ratings accepts custom limit."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()
        result = rater.get_recent_ratings("user123", limit=10)

        # Default implementation returns empty list
        assert result == []

    def test_get_card_ratings_with_rating_type(self):
        """get_card_ratings accepts rating_type parameter."""
        from local_deep_research.news.rating_system.base_rater import (
            QualityRatingSystem,
        )

        rater = QualityRatingSystem()
        result = rater.get_card_ratings("card123", rating_type="quality")

        # Default implementation returns default aggregation
        assert result == {"total": 0, "average": None}
