"""Tests for Pydantic data models (backend/models.py)."""
import pytest
from pydantic import ValidationError
from models import (
    UserCreate,
    UserUpdate,
    EncryptionMetadata,
    JournalEntryCreate,
    JournalEntryUpdate,
    MoodEnum,
)


# ---------------------------------------------------------------------------
# UserCreate
# ---------------------------------------------------------------------------

class TestUserCreate:
    def test_requires_email(self):
        with pytest.raises(ValidationError):
            UserCreate()

    def test_valid_email_only(self):
        user = UserCreate(email="test@example.com")
        assert user.email == "test@example.com"

    def test_display_name_is_optional(self):
        user = UserCreate(email="test@example.com")
        assert user.displayName is None

    def test_photo_url_is_optional(self):
        user = UserCreate(email="test@example.com")
        assert user.photoURL is None

    def test_all_fields(self):
        user = UserCreate(
            email="test@example.com",
            displayName="Alice",
            photoURL="https://example.com/pic.jpg",
        )
        assert user.displayName == "Alice"
        assert user.photoURL == "https://example.com/pic.jpg"


# ---------------------------------------------------------------------------
# UserUpdate
# ---------------------------------------------------------------------------

class TestUserUpdate:
    def test_all_fields_optional(self):
        update = UserUpdate()
        assert update.displayName is None
        assert update.photoURL is None
        assert update.theme is None

    def test_update_only_theme(self):
        update = UserUpdate(theme="dark")
        assert update.theme == "dark"
        assert update.displayName is None

    def test_update_only_display_name(self):
        update = UserUpdate(displayName="New Name")
        assert update.displayName == "New Name"
        assert update.theme is None

    def test_model_dump_excludes_unset(self):
        update = UserUpdate(theme="dark")
        dumped = update.model_dump(exclude_unset=True)
        assert "theme" in dumped
        assert "displayName" not in dumped


# ---------------------------------------------------------------------------
# EncryptionMetadata
# ---------------------------------------------------------------------------

class TestEncryptionMetadata:
    def test_requires_ciphertext(self):
        with pytest.raises(ValidationError):
            EncryptionMetadata(nonce="abc")

    def test_requires_nonce(self):
        with pytest.raises(ValidationError):
            EncryptionMetadata(ciphertext="abc")

    def test_requires_both_ciphertext_and_nonce(self):
        with pytest.raises(ValidationError):
            EncryptionMetadata()

    def test_default_algorithm_is_xsalsa20(self):
        meta = EncryptionMetadata(ciphertext="abc", nonce="xyz")
        assert meta.algorithm == "XSalsa20-Poly1305"

    def test_default_encrypted_is_true(self):
        meta = EncryptionMetadata(ciphertext="abc", nonce="xyz")
        assert meta.encrypted is True

    def test_valid_full_metadata(self):
        meta = EncryptionMetadata(
            encrypted=True,
            ciphertext="dGVzdA==",
            nonce="bm9uY2U=",
            algorithm="XSalsa20-Poly1305",
        )
        assert meta.ciphertext == "dGVzdA=="
        assert meta.nonce == "bm9uY2U="

    def test_custom_algorithm_accepted(self):
        meta = EncryptionMetadata(ciphertext="abc", nonce="xyz", algorithm="AES-256-GCM")
        assert meta.algorithm == "AES-256-GCM"


# ---------------------------------------------------------------------------
# JournalEntryCreate
# ---------------------------------------------------------------------------

class TestJournalEntryCreate:
    def test_all_fields_optional(self):
        entry = JournalEntryCreate()
        assert entry.title is None
        assert entry.content is None
        assert entry.encryption is None
        assert entry.mood is None

    def test_encrypted_entry_has_no_plaintext(self):
        """Encrypted entries legitimately have no title or content."""
        entry = JournalEntryCreate(
            encryption=EncryptionMetadata(ciphertext="abc", nonce="xyz")
        )
        assert entry.title is None
        assert entry.content is None
        assert entry.encryption is not None

    def test_valid_mood_values(self):
        for mood in ("happy", "sad", "neutral", "anxious", "grateful"):
            entry = JournalEntryCreate(mood=mood)
            assert entry.mood == mood

    def test_invalid_mood_raises_validation_error(self):
        with pytest.raises(ValidationError):
            JournalEntryCreate(mood="ecstatic")

    def test_default_is_public_is_false(self):
        entry = JournalEntryCreate()
        assert entry.isPublic is False

    def test_tags_default_is_none(self):
        entry = JournalEntryCreate()
        assert entry.tags is None

    def test_tags_list_accepted(self):
        entry = JournalEntryCreate(tags=["family", "work", "health"])
        assert entry.tags == ["family", "work", "health"]


# ---------------------------------------------------------------------------
# JournalEntryUpdate
# ---------------------------------------------------------------------------

class TestJournalEntryUpdate:
    def test_all_fields_optional(self):
        update = JournalEntryUpdate()
        assert update.title is None
        assert update.mood is None

    def test_update_mood_only(self):
        update = JournalEntryUpdate(mood="happy")
        dumped = update.model_dump(exclude_unset=True)
        assert dumped == {"mood": MoodEnum.happy}

    def test_invalid_mood_raises_error(self):
        with pytest.raises(ValidationError):
            JournalEntryUpdate(mood="angry")

    def test_update_encryption(self):
        update = JournalEntryUpdate(
            encryption=EncryptionMetadata(ciphertext="new_ct", nonce="new_nonce")
        )
        assert update.encryption.ciphertext == "new_ct"


# ---------------------------------------------------------------------------
# MoodEnum
# ---------------------------------------------------------------------------

class TestMoodEnum:
    def test_all_enum_values(self):
        assert MoodEnum.happy == "happy"
        assert MoodEnum.sad == "sad"
        assert MoodEnum.neutral == "neutral"
        assert MoodEnum.anxious == "anxious"
        assert MoodEnum.grateful == "grateful"

    def test_enum_used_in_entry_create(self):
        entry = JournalEntryCreate(mood=MoodEnum.grateful)
        assert entry.mood == "grateful"
