# tests/test_queryset_delete.py
"""Tests for QuerySet.delete() method."""

import pytest

from atomsql import Database, Model
from atomsql.orm.fields import BooleanField, CharField, IntegerField, TextField


class TestQuerySetDeleteBasic:
    """Basic delete functionality tests."""

    def test_delete_empty_queryset(self):
        """Test delete on empty queryset returns 0."""

        class User(Model):
            name = CharField(max_length=100)

        db = Database("sqlite:///:memory:")
        db.register(User)

        # No users exist, delete should return 0
        deleted = User.filter(name="nonexistent").delete()
        assert deleted == 0

    def test_delete_single_record(self):
        """Test delete single record by filter."""

        class User(Model):
            name = CharField(max_length=100)
            email = CharField(max_length=150)

        db = Database("sqlite:///:memory:")
        db.register(User)

        # Create users
        User.create(name="Alice", email="alice@test.com")
        User.create(name="Bob", email="bob@test.com")

        # Delete one user
        deleted = User.filter(name="Alice").delete()
        assert deleted == 1

        # Verify only one user remains
        remaining = User.filter().all()
        assert len(remaining) == 1
        assert remaining[0].name == "Bob"

    def test_delete_multiple_records(self):
        """Test delete multiple records matching filter."""

        class Product(Model):
            name = CharField(max_length=100)
            category = CharField(max_length=50)
            active = BooleanField(default=True)

        db = Database("sqlite:///:memory:")
        db.register(Product)

        # Create products
        Product.create(name="Widget A", category="widgets", active=True)
        Product.create(name="Widget B", category="widgets", active=True)
        Product.create(name="Gadget A", category="gadgets", active=True)
        Product.create(name="Widget C", category="widgets", active=False)

        # Delete all active widgets
        deleted = Product.filter(category="widgets", active=True).delete()
        assert deleted == 2

        # Verify remaining products
        remaining = Product.filter().all()
        assert len(remaining) == 2
        names = [p.name for p in remaining]
        assert "Gadget A" in names
        assert "Widget C" in names

    def test_delete_all_records(self):
        """Test delete all records in table."""

        class Item(Model):
            name = CharField(max_length=100)

        db = Database("sqlite:///:memory:")
        db.register(Item)

        # Create items
        Item.create(name="Item 1")
        Item.create(name="Item 2")
        Item.create(name="Item 3")

        # Delete all
        deleted = Item.filter().delete()
        assert deleted == 3

        # Verify table is empty
        assert Item.filter().count() == 0


class TestQuerySetDeleteWithLookups:
    """Test delete with various filter lookups."""

    def test_delete_with_gt_filter(self):
        """Test delete with greater than filter."""

        class Score(Model):
            player = CharField(max_length=100)
            points = IntegerField()

        db = Database("sqlite:///:memory:")
        db.register(Score)

        # Create scores
        Score.create(player="Alice", points=100)
        Score.create(player="Bob", points=200)
        Score.create(player="Charlie", points=300)
        Score.create(player="David", points=150)

        # Delete scores > 150
        deleted = Score.filter(points__gt=150).delete()
        assert deleted == 2

        # Verify remaining
        remaining = Score.filter().all()
        assert len(remaining) == 2
        points = [s.points for s in remaining]
        assert 100 in points
        assert 150 in points

    def test_delete_with_in_filter(self):
        """Test delete with IN filter."""

        class User(Model):
            name = CharField(max_length=100)
            role = CharField(max_length=50)

        db = Database("sqlite:///:memory:")
        db.register(User)

        # Create users
        User.create(name="Alice", role="admin")
        User.create(name="Bob", role="user")
        User.create(name="Charlie", role="moderator")
        User.create(name="David", role="user")

        # Delete users with specific roles
        deleted = User.filter(role__in=["admin", "moderator"]).delete()
        assert deleted == 2

        # Verify remaining
        remaining = User.filter().all()
        assert len(remaining) == 2
        for user in remaining:
            assert user.role == "user"

    def test_delete_with_contains_filter(self):
        """Test delete with CONTAINS filter."""

        class Article(Model):
            title = CharField(max_length=200)
            content = TextField()

        db = Database("sqlite:///:memory:")
        db.register(Article)

        # Create articles
        Article.create(title="Python Tutorial", content="Learn Python basics")
        Article.create(title="JavaScript Guide", content="JavaScript fundamentals")
        Article.create(title="Python Advanced", content="Advanced Python concepts")
        Article.create(title="HTML Basics", content="Basic HTML structure")

        # Delete articles with "Python" in title
        deleted = Article.filter(title__contains="Python").delete()
        assert deleted == 2

        # Verify remaining
        remaining = Article.filter().all()
        assert len(remaining) == 2
        titles = [a.title for a in remaining]
        assert "JavaScript Guide" in titles
        assert "HTML Basics" in titles


class TestQuerySetDeleteWithNullable:
    """Test delete with nullable fields."""

    def test_delete_null_fields(self):
        """Test delete records with null values."""

        class Profile(Model):
            username = CharField(max_length=100)
            bio = CharField(max_length=500, nullable=True)

        db = Database("sqlite:///:memory:")
        db.register(Profile)

        # Create profiles - some with null bio
        Profile.create(username="user1", bio="I am user 1")
        Profile.create(username="user2", bio=None)
        Profile.create(username="user3", bio="I am user 3")
        Profile.create(username="user4", bio=None)

        # Note: We can't easily test for NULL with current lookup system
        # This is more of a structural test
        deleted = Profile.filter(username__in=["user2", "user4"]).delete()
        assert deleted == 2

        remaining = Profile.filter().all()
        assert len(remaining) == 2


class TestQuerySetDeleteValidation:
    """Validation and error handling tests."""

    def test_delete_with_invalid_field(self):
        """Test delete with non-existent field raises error."""

        class User(Model):
            name = CharField(max_length=100)

        db = Database("sqlite:///:memory:")
        db.register(User)

        with pytest.raises(ValueError) as exc_info:
            User.filter(nonexistent="value").delete()

        assert "does not exist" in str(exc_info.value)


class TestQuerySetDeleteTransactions:
    """Test delete with transactions."""

    def test_delete_in_transaction_commit(self):
        """Test delete within committed transaction."""

        class User(Model):
            name = CharField(max_length=100)
            status = CharField(max_length=50)

        db = Database("sqlite:///:memory:")
        db.register(User)

        # Create users
        User.create(name="Alice", status="active")
        User.create(name="Bob", status="inactive")
        User.create(name="Charlie", status="inactive")

        # Delete within transaction
        with db.transaction():
            deleted = User.filter(status="inactive").delete()
            assert deleted == 2

        # Verify deletion committed
        remaining = User.filter().all()
        assert len(remaining) == 1
        assert remaining[0].name == "Alice"

    def test_delete_transaction_rollback(self):
        """Test delete rollback on transaction failure."""

        class User(Model):
            name = CharField(max_length=100)
            status = CharField(max_length=50)

        db = Database("sqlite:///:memory:")
        db.register(User)

        # Create users
        User.create(name="Alice", status="active")
        User.create(name="Bob", status="inactive")
        User.create(name="Charlie", status="inactive")

        # Delete in transaction that fails
        try:
            with db.transaction():
                deleted = User.filter(status="inactive").delete()
                assert deleted == 2
                raise Exception("Force rollback")
        except Exception:
            pass

        # Verify deletion was rolled back
        remaining = User.filter().all()
        assert len(remaining) == 3


class TestQuerySetDeleteChaining:
    """Test delete with QuerySet method chaining."""

    def test_delete_after_chaining(self):
        """Test delete after chaining filter operations."""

        class Order(Model):
            customer = CharField(max_length=100)
            amount = IntegerField()
            status = CharField(max_length=50)

        db = Database("sqlite:///:memory:")
        db.register(Order)

        # Create orders
        Order.create(customer="Alice", amount=100, status="pending")
        Order.create(customer="Bob", amount=200, status="completed")
        Order.create(customer="Alice", amount=150, status="pending")
        Order.create(customer="Charlie", amount=50, status="pending")

        # Chain filters and delete
        deleted = Order.filter(status="pending").filter(amount__gt=75).delete()
        assert deleted == 2

        # Verify remaining orders
        remaining = Order.filter().all()
        assert len(remaining) == 2
        # Should have Bob's completed order and Charlie's small pending order


class TestQuerySetDeletePerformance:
    """Performance tests for delete operations."""

    def test_delete_large_batch(self):
        """Test delete with large number of records."""

        class LogEntry(Model):
            message = CharField(max_length=200)
            level = IntegerField()

        db = Database("sqlite:///:memory:")
        db.register(LogEntry)

        # Create many log entries
        for i in range(100):
            LogEntry.create(message=f"Log message {i}", level=i % 5)

        # Delete all entries with level >= 3
        deleted = LogEntry.filter(level__gte=3).delete()
        # Should delete entries with level 3 and 4 (every 5th entry has level 3, every 5th has level 4)
        assert deleted == 40

        # Verify remaining
        remaining = LogEntry.filter().count()
        assert remaining == 60


class TestQuerySetDeleteReturnValue:
    """Test delete return value accuracy."""

    def test_delete_return_count_accuracy(self):
        """Test that delete returns accurate count of deleted records."""

        class Task(Model):
            title = CharField(max_length=100)
            completed = BooleanField(default=False)
            priority = IntegerField()

        db = Database("sqlite:///:memory:")
        db.register(Task)

        # Create tasks with various priorities
        tasks_created = 0
        for priority in [1, 2, 3, 1, 2, 3, 1]:
            Task.create(
                title=f"Task {tasks_created}", completed=False, priority=priority
            )
            tasks_created += 1

        # Delete all priority 1 tasks
        deleted = Task.filter(priority=1).delete()
        assert deleted == 3

        # Verify count
        remaining = Task.filter().count()
        assert remaining == 4


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
