"""Tests for the Category 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.category import (
    Category,
    CategoryResponse,
    CategoryListResponse,
    CategoryTitleValidator,
    CategoryNameValidator,
    CategoryRepository,
)


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

def test_category_from_db_full():
    cat = Category.from_db({
        "id": 1, "title": "Tech", "name": "technology",
        "parent_id": None,
    })
    assert cat.id == 1
    assert cat.title == "Tech"
    assert cat.name == "technology"
    assert cat.parent_id is None


def test_category_from_db_with_parent():
    cat = Category.from_db({
        "id": 2, "title": "Python", "name": "python",
        "parent_id": 1,
    })
    assert cat.parent_id == 1


def test_category_to_db_round_trip():
    cat = Category(id=1, title="Tech", name="technology")
    row = cat.to_db()
    restored = Category.from_db(row)
    assert restored == cat


# ===========================================================================
# Schemas
# ===========================================================================

def test_category_response_from_dict():
    resp = CategoryResponse.model_validate({
        "id": 1, "title": "Tech", "name": "technology",
    })
    assert resp.id == 1
    assert resp.title == "Tech"


def test_category_response_to_dict():
    resp = CategoryResponse(id=1, title="Tech", name="technology")
    data = resp.to_dict()
    assert data["title"] == "Tech"
    assert data["name"] == "technology"


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

async def test_title_validator_strips():
    v = CategoryTitleValidator()
    result = await v.validate("  Tech  ")
    assert result == "Tech"


async def test_title_validator_rejects_blank():
    v = CategoryTitleValidator()
    with pytest.raises(ValidationError):
        await v.validate("")


async def test_name_validator_strips():
    v = CategoryNameValidator()
    result = await v.validate("  technology  ")
    assert result == "technology"


async def test_name_validator_rejects_blank():
    v = CategoryNameValidator()
    with pytest.raises(ValidationError):
        await v.validate("")


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

class TestCategoryRepository:

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

    async def test_list_all(self, repo, db_query):
        db_query.client.find_many.return_value = [
            {"id": 1, "title": "Tech", "name": "technology"},
            {"id": 2, "title": "Science", "name": "science"},
        ]
        db_query.client.count.return_value = 2
        results, total = await repo.list_all()
        assert len(results) == 2
        assert total == 2

    async def test_get_children(self, repo, db_query):
        db_query.client.find_many.return_value = [
            {"id": 2, "title": "Python", "name": "python", "parent_id": 1},
        ]
        children = await repo.get_children(1)
        assert len(children) == 1
        assert children[0].parent_id == 1

    async def test_get_by_id_found(self, repo, db_query):
        db_query.client.find_one.return_value = {
            "id": 1, "title": "Tech", "name": "technology",
        }
        cat = await repo.get_by_id(1)
        assert cat is not None
        assert cat.id == 1

    async def test_get_by_id_not_found(self, repo, db_query):
        db_query.client.find_by_id.return_value = None
        cat = await repo.get_by_id(999)
        assert cat is None
