"""Tests for the Bookmark domain — model, schemas, validators, and repository."""
from __future__ import annotations

from datetime import datetime

import pytest
from pydantic import ValidationError as PydanticValidationError

from repo_core import ValidationError
from repo_core.domains.bookmark import (
    Bookmark,
    BookmarkTargetType,
    CreateBookmarkRequest,
    BookmarkResponse,
    BookmarkCheckResponse,
    BookmarkTargetIdValidator,
    BookmarkRepository,
)


# ===========================================================================
# Model
# ===========================================================================

def test_bookmark_from_db_full():
    bm = Bookmark.from_db({
        "id": 1, "user_id": 42,
        "target_type": "event", "target_id": "789",
    })
    assert bm.id == 1
    assert bm.user_id == 42
    assert bm.target_type == BookmarkTargetType.EVENT
    assert bm.target_id == "789"


def test_bookmark_from_db_default_target_type():
    bm = Bookmark.from_db({
        "id": 2, "user_id": 7, "target_id": "123",
    })
    assert bm.target_type == BookmarkTargetType.EVENT


def test_bookmark_to_db_round_trip():
    bm = Bookmark(id=1, user_id=42, target_id="789")
    row = bm.to_db()
    restored = Bookmark.from_db(row)
    assert restored == bm


# ===========================================================================
# Schemas — CreateBookmarkRequest
# ===========================================================================

def test_create_bookmark_request_valid():
    req = CreateBookmarkRequest(event_id="42")
    assert req.event_id == "42"


def test_create_bookmark_request_rejects_empty():
    with pytest.raises(PydanticValidationError):
        CreateBookmarkRequest(event_id="")


def test_create_bookmark_request_rejects_extra_fields():
    with pytest.raises(PydanticValidationError):
        CreateBookmarkRequest(event_id="42", rogue=True)


# ===========================================================================
# Schemas — BookmarkResponse
# ===========================================================================

def test_bookmark_response_from_dict():
    resp = BookmarkResponse.model_validate({
        "id": 1, "user_id": 42,
        "target_type": "event", "target_id": "789",
    })
    assert resp.id == 1
    assert resp.target_type == BookmarkTargetType.EVENT


def test_bookmark_response_to_dict():
    resp = BookmarkResponse(id=1, user_id=42, target_type=BookmarkTargetType.EVENT, target_id="789")
    data = resp.to_dict()
    assert data["target_type"] == "event"


# ===========================================================================
# Validators
# ===========================================================================

async def test_target_id_validator_strips():
    v = BookmarkTargetIdValidator()
    result = await v.validate("  42  ")
    assert result == "42"


async def test_target_id_validator_rejects_blank():
    v = BookmarkTargetIdValidator()
    with pytest.raises(ValidationError):
        await v.validate("")


# ===========================================================================
# Repository
# ===========================================================================

class TestBookmarkRepository:

    @pytest.fixture
    def repo(self, db_query):
        return BookmarkRepository(model=Bookmark, db=db_query)

    async def test_save_bookmark_creates_new(self, repo, db_query):
        db_query.client.find_one.return_value = None
        db_query.client.create.return_value = {
            "id": 1, "user_id": 42,
            "target_type": "event", "target_id": "789",
        }
        result = await repo.save_bookmark(user_id=42, event_id="789")
        assert result.id == 1
        assert result.target_type == BookmarkTargetType.EVENT

    async def test_save_bookmark_idempotent(self, repo, db_query):
        db_query.client.find_one.return_value = {
            "id": 5, "user_id": 42,
            "target_type": "event", "target_id": "789",
        }
        result = await repo.save_bookmark(user_id=42, event_id="789")
        assert result.id == 5
        db_query.client.create.assert_not_called()

    async def test_remove_bookmark(self, repo, db_query):
        db_query.client.delete.return_value = 1
        assert await repo.remove_bookmark(user_id=42, event_id="789") is True

    async def test_remove_bookmark_not_found(self, repo, db_query):
        db_query.client.delete.return_value = 0
        assert await repo.remove_bookmark(user_id=42, event_id="789") is False

    async def test_is_bookmarked_true(self, repo, db_query):
        db_query.client.find_one.return_value = {"id": 1, "user_id": 42, "target_type": "event", "target_id": "789"}
        assert await repo.is_bookmarked(42, "789") is True

    async def test_is_bookmarked_false(self, repo, db_query):
        db_query.client.find_one.return_value = None
        assert await repo.is_bookmarked(42, "789") is False

    async def test_list_bookmarks(self, repo, db_query):
        db_query.client.find_many.return_value = [
            {"id": 1, "user_id": 42, "target_type": "event", "target_id": "1"},
            {"id": 2, "user_id": 42, "target_type": "event", "target_id": "2"},
        ]
        db_query.client.count.return_value = 2
        results, total = await repo.list_bookmarks(42)
        assert len(results) == 2
        assert total == 2