from __future__ import annotations

import pytest

from django_anyllm.permissions import PermissionDenied
from django_anyllm.tools.crud import (
    create_instance,
    delete_instance,
    get_instance,
    query_instances,
    update_instance,
)

from .models import Author


class TestQueryInstances:
    """Tests for query_instances."""

    def test_basic_query(self, db, registry):
        Author.objects.create(name="Alice", email="alice@example.com")
        Author.objects.create(name="Bob", email="bob@example.com")
        results = query_instances(registry, "tests", "author")
        assert len(results) == 2

    def test_filter(self, db, registry):
        Author.objects.create(name="Alice", email="alice@example.com")
        Author.objects.create(name="Bob", email="bob@example.com")
        results = query_instances(
            registry, "tests", "author", filters={"name__icontains": "ali"}
        )
        assert len(results) == 1
        assert results[0]["name"] == "Alice"

    def test_invalid_filter_key(self, db, registry):
        with pytest.raises(ValueError, match="Invalid filter key"):
            query_instances(registry, "tests", "author", filters={"nope": "value"})

    def test_limit_respected(self, db, registry):
        for i in range(5):
            Author.objects.create(name=f"Author {i}", email=f"a{i}@example.com")
        results = query_instances(registry, "tests", "author", limit=2)
        assert len(results) == 2

    def test_permission_denied(self, db, registry):
        registry.register(Author, permissions={"create"})
        with pytest.raises(PermissionDenied):
            query_instances(registry, "tests", "author")


class TestGetInstance:
    """Tests for get_instance."""

    def test_found(self, db, registry):
        author = Author.objects.create(name="Alice", email="alice@example.com")
        result = get_instance(registry, "tests", "author", author.pk)
        assert result is not None
        assert result["name"] == "Alice"

    def test_not_found(self, db, registry):
        result = get_instance(registry, "tests", "author", 9999)
        assert result is None


class TestCreateInstance:
    """Tests for create_instance."""

    def test_create(self, db, registry):
        result = create_instance(
            registry, "tests", "author", {"name": "Alice", "email": "alice@example.com"}
        )
        assert result["name"] == "Alice"
        assert Author.objects.filter(pk=result["pk"]).exists()

    def test_validation_error(self, db, registry):
        with pytest.raises(ValueError, match="Validation error"):
            create_instance(registry, "tests", "author", {"name": "Alice"})

    def test_create_with_choices(self, db, registry):
        author = Author.objects.create(name="Author", email="a@example.com")
        result = create_instance(
            registry,
            "tests",
            "book",
            {
                "title": "New Book",
                "author": author.pk,
                "genre": "science",
            },
        )
        assert result["title"] == "New Book"


class TestUpdateInstance:
    """Tests for update_instance."""

    def test_update(self, db, registry):
        author = Author.objects.create(name="Alice", email="alice@example.com")
        result = update_instance(
            registry, "tests", "author", author.pk, {"name": "Alicia"}
        )
        assert result["name"] == "Alicia"
        author.refresh_from_db()
        assert author.name == "Alicia"

    def test_not_found(self, db, registry):
        with pytest.raises(ValueError, match="does not exist"):
            update_instance(registry, "tests", "author", 9999, {"name": "Ghost"})


class TestDeleteInstance:
    """Tests for delete_instance."""

    def test_delete(self, db, registry):
        author = Author.objects.create(name="Alice", email="alice@example.com")
        assert delete_instance(registry, "tests", "author", author.pk) is True
        assert not Author.objects.filter(pk=author.pk).exists()

    def test_not_found(self, db, registry):
        assert delete_instance(registry, "tests", "author", 9999) is False
