"""
Test the on_delete cascade options for ForeignKey fields.

These tests validate all four cascade behaviors:
- CASCADE: Delete dependent objects when the parent is deleted
- SET_NULL: Set the foreign key to NULL when the parent is deleted
- SET_DEFAULT: Set the foreign key to its default value when the parent is deleted
- PROTECT: Prevent deletion of the parent when dependent objects exist
"""

import pytest
from atomsql.orm.models import Model
from atomsql.orm.fields import CharField, ForeignKey
from atomsql.orm.fields import CASCADE, SET_NULL, SET_DEFAULT, PROTECT
from atomsql.core.db import Database


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


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


class Product(Model):
    name = CharField(max_length=100)
    # Each product has different cascade behaviors for different relationships
    category_cascade = ForeignKey(Category, on_delete=CASCADE)
    category_set_null = ForeignKey(Category, nullable=True, on_delete=SET_NULL)
    category_set_default = ForeignKey(DefaultCategory, default=1, on_delete=SET_DEFAULT)
    category_protect = ForeignKey(Category, on_delete=PROTECT)


@pytest.fixture
def db():
    database = Database("sqlite:///:memory:")

    # Bind the database to the Models
    Category._db = database
    DefaultCategory._db = database
    Product._db = database

    # Register and create tables
    database.register(Category)
    database.register(DefaultCategory)
    database.register(Product)
    database.create_all()

    yield database

    # Teardown
    database.close()
    Category._db = None
    DefaultCategory._db = None
    Product._db = None


def test_cascade_delete(db):
    """Test CASCADE on_delete behavior."""
    # Create test data
    electronics = Category(name="Electronics")
    electronics.save()

    books = Category(name="Books")
    books.save()

    default_cat = DefaultCategory(name="Default")
    default_cat.save()

    product = Product(
        name="Phone",
        category_cascade=electronics,
        category_set_null=books,
        category_set_default=default_cat,
        category_protect=electronics,
    )
    product.save()

    # Delete the electronics category - should cascade delete the product
    electronics.delete()

    # Product should be deleted due to CASCADE
    remaining_products = Product.all().count()
    assert remaining_products == 0


def test_set_null_delete(db):
    """Test SET_NULL on_delete behavior."""
    # Create test data
    electronics = Category(name="Electronics")
    electronics.save()

    books = Category(name="Books")
    books.save()

    default_cat = DefaultCategory(name="Default")
    default_cat.save()

    product = Product(
        name="Phone",
        category_cascade=electronics,
        category_set_null=books,
        category_set_default=default_cat,
        category_protect=electronics,
    )
    product.save()

    # Delete the books category - should set category_set_null to NULL
    books.delete()

    # Product should still exist but with category_set_null = NULL
    remaining_products = Product.all().count()
    assert remaining_products == 1

    product_after_delete = Product.get(product.id)
    assert product_after_delete.category_set_null is None
    assert product_after_delete.category_cascade == electronics
    assert product_after_delete.category_set_default == default_cat
    assert product_after_delete.category_protect == electronics


def test_set_default_delete(db):
    """Test SET_DEFAULT on_delete behavior."""
    # Create test data
    electronics = Category(name="Electronics")
    electronics.save()

    books = Category(name="Books")
    books.save()

    default_cat = DefaultCategory(name="Default")
    default_cat.save()

    other_default = DefaultCategory(name="Other Default")
    other_default.save()

    product = Product(
        name="Phone",
        category_cascade=electronics,
        category_set_null=books,
        category_set_default=other_default,  # Set to non-default value
        category_protect=electronics,
    )
    product.save()

    # Delete other_default category - should set category_set_default to default (id=1)
    other_default.delete()

    # Product should still exist with category_set_default reset to default
    remaining_products = Product.all().count()
    assert remaining_products == 1

    product_after_delete = Product.get(product.id)
    assert product_after_delete.category_set_default.id == 1  # Reset to default


def test_protect_delete(db):
    """Test PROTECT on_delete behavior."""
    # Create test data
    electronics = Category(name="Electronics")
    electronics.save()

    books = Category(name="Books")
    books.save()

    default_cat = DefaultCategory(name="Default")
    default_cat.save()

    product = Product(
        name="Phone",
        category_cascade=books,  # Point to books for cascade
        category_set_null=books,
        category_set_default=default_cat,
        category_protect=electronics,  # Protect electronics
    )
    product.save()

    # Try to delete electronics - should fail due to PROTECT
    with pytest.raises(
        ValueError, match="Cannot delete Category instance.*PROTECT constraint"
    ):
        electronics.delete()

    # Electronics should still exist
    remaining_categories = Category.filter(name="Electronics").count()
    assert remaining_categories == 1

    # Product should still exist
    remaining_products = Product.all().count()
    assert remaining_products == 1


def test_cascade_validation_set_null_requires_nullable():
    """Test that SET_NULL requires nullable=True."""
    with pytest.raises(
        ValueError, match="Cannot use SET_NULL on_delete with nullable=False"
    ):

        class BadProduct(Model):
            category = ForeignKey(Category, nullable=False, on_delete=SET_NULL)


def test_multiple_cascade_behaviors(db):
    """Test complex scenario with multiple cascade behaviors."""
    # Create categories
    electronics = Category(name="Electronics")
    electronics.save()

    books = Category(name="Books")
    books.save()

    default_cat = DefaultCategory(name="Default")
    default_cat.save()

    protected_cat = Category(name="Protected")
    protected_cat.save()

    # Create multiple products with different relationships
    phone = Product(
        name="Phone",
        category_cascade=electronics,
        category_set_null=books,
        category_set_default=default_cat,
        category_protect=protected_cat,
    )
    phone.save()

    laptop = Product(
        name="Laptop",
        category_cascade=books,
        category_set_null=electronics,
        category_set_default=default_cat,
        category_protect=protected_cat,
    )
    laptop.save()

    # Delete electronics - should cascade delete phone, set laptop's category_set_null to NULL
    electronics.delete()

    # Only laptop should remain
    remaining_products = Product.all()
    assert len(remaining_products) == 1
    assert remaining_products[0].name == "Laptop"
    assert remaining_products[0].category_set_null is None
