# tests/test_bulk_create_postgres.py
"""PostgreSQL-specific tests for bulk_create() method."""

import pytest

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

pytest.importorskip("psycopg")


@pytest.fixture(autouse=True)
def reset_models():
    """Reset model registry before each test."""
    ModelMeta.models.clear()
    yield
    ModelMeta.models.clear()


@pytest.fixture
def postgres_db():
    """Create a PostgreSQL test database."""
    # Use a test database - adjust connection string as needed
    try:
        db = Database(
            "postgresql://atomsql_user:yourpassword@localhost:5432/atomsql_test"
        )

        # Drop all existing tables before starting tests
        tables = db.backend.get_tables()
        if tables:
            for table in tables:
                db.backend.execute(f'DROP TABLE IF EXISTS "{table}" CASCADE')
            db.backend.commit()

        yield db

        # Cleanup after test
        db.close()
    except Exception as e:
        pytest.skip(f"PostgreSQL not available: {e}")


class TestPostgresBulkCreate:
    """Test bulk_create with PostgreSQL-specific features."""

    def test_postgres_bulk_create_uses_returning(self, postgres_db):
        """Test that PostgreSQL uses RETURNING clause."""

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

        postgres_db.register(User)

        users = [
            User(name="Alice", email="alice@example.com"),
            User(name="Bob", email="bob@example.com"),
            User(name="Charlie", email="charlie@example.com"),
        ]

        result = User.bulk_create(users)

        assert len(result) == 3
        assert all(user.id is not None for user in result)
        assert result[0].name == "Alice"
        assert result[1].name == "Bob"
        assert result[2].name == "Charlie"

    def test_postgres_bulk_create_sequential_ids(self, postgres_db):
        """Test that IDs from RETURNING are sequential."""

        class Product(Model):
            name = CharField(max_length=100)
            price = IntegerField()

        postgres_db.register(Product)

        products = [Product(name=f"Product {i}", price=i * 10) for i in range(10)]
        result = Product.bulk_create(products)

        ids = [p.id for p in result]
        # IDs should be sequential
        for i in range(len(ids) - 1):
            assert ids[i + 1] == ids[i] + 1

    def test_postgres_bulk_create_large_batch(self, postgres_db):
        """Test bulk_create with large batch on PostgreSQL."""

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

        postgres_db.register(LogEntry)

        # Create 500 entries
        entries = [
            LogEntry(message=f"Log message {i}", level=i % 5) for i in range(500)
        ]
        result = LogEntry.bulk_create(entries)

        assert len(result) == 500
        assert all(entry.id is not None for entry in result)

        # Verify all in database
        count = LogEntry.filter().count()
        assert count == 500

    def test_postgres_bulk_create_with_text_fields(self, postgres_db):
        """Test bulk_create with TEXT fields on PostgreSQL."""

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

        postgres_db.register(Article)

        articles = [
            Article(title=f"Article {i}", content=f"Long content for article {i}" * 100)
            for i in range(20)
        ]
        result = Article.bulk_create(articles)

        assert len(result) == 20
        # Verify content is preserved
        retrieved = list(Article.filter())
        assert len(retrieved) == 20
        assert all(len(a.content) > 100 for a in retrieved)

    def test_postgres_bulk_create_mixed_with_regular_create(self, postgres_db):
        """Test mixing bulk_create with regular create on PostgreSQL."""

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

        postgres_db.register(User)

        # Regular create
        User.create(username="regular1")

        # Bulk create
        bulk_users = [User(username=f"bulk{i}") for i in range(5)]
        User.bulk_create(bulk_users)

        # Another regular create
        User.create(username="regular2")

        # All should be in database
        all_users = list(User.filter())
        assert len(all_users) == 7

    def test_postgres_bulk_create_transaction_rollback(self, postgres_db):
        """Test that bulk_create can be rolled back in transaction."""

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

        postgres_db.register(User)

        # Create some initial users
        User.bulk_create([User(name="Initial")])

        try:
            with postgres_db.transaction():
                # Bulk create inside transaction
                users = [User(name=f"TxUser{i}") for i in range(5)]
                User.bulk_create(users)

                # Force rollback by raising exception
                raise Exception("Forced rollback")
        except Exception:
            pass

        # Should only have initial user
        count = User.filter().count()
        assert count == 1

    def test_postgres_bulk_create_returns_correct_order(self, postgres_db):
        """Test that returned instances maintain the same order."""

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

        postgres_db.register(Item)

        items = [Item(name=f"Item{i}", order=i) for i in range(20)]
        result = Item.bulk_create(items)

        # Verify order is maintained
        for i, item in enumerate(result):
            assert item.name == f"Item{i}"
            assert item.order == i

    def test_postgres_bulk_create_with_identity_map(self, postgres_db):
        """Test that bulk_create adds instances to identity map."""

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

        postgres_db.register(User)

        users = [User(name=f"User{i}") for i in range(10)]
        created = User.bulk_create(users)

        # Get should return same instances from identity map
        for user in created:
            retrieved = User.get(user.id)
            assert retrieved is user

    def test_postgres_backend_bulk_insert_method(self, postgres_db):
        """Test the PostgreSQL backend's bulk_insert method directly."""

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

        postgres_db.register(User)

        # Build SQL manually
        sql = 'INSERT INTO "user" ("name") VALUES (%s), (%s), (%s)'
        values = ["Alice", "Bob", "Charlie"]

        # Call bulk_insert directly
        returned_ids = postgres_db.backend.bulk_insert(sql, values, 3)

        assert len(returned_ids) == 3
        assert all(id is not None for id in returned_ids)
        # IDs should be sequential
        for i in range(len(returned_ids) - 1):
            assert returned_ids[i + 1] == returned_ids[i] + 1


class TestPostgresVsSQLite:
    """Compare PostgreSQL and SQLite bulk_create behavior."""

    def test_both_backends_produce_same_results(self, postgres_db):
        """Test that both backends work the same way."""

        # Test with PostgreSQL
        class PgUser(Model):
            name = CharField(max_length=100)

        postgres_db.register(PgUser)
        pg_users = [PgUser(name=f"User{i}") for i in range(10)]
        pg_result = PgUser.bulk_create(pg_users)

        # Test with SQLite
        class SqliteUser(Model):
            name = CharField(max_length=100)

        sqlite_db = Database("sqlite:///:memory:")
        sqlite_db.register(SqliteUser)
        sqlite_users = [SqliteUser(name=f"User{i}") for i in range(10)]
        sqlite_result = SqliteUser.bulk_create(sqlite_users)

        # Both should create 10 users with IDs
        assert len(pg_result) == 10
        assert len(sqlite_result) == 10
        assert all(u.id is not None for u in pg_result)
        assert all(u.id is not None for u in sqlite_result)

        # Both should have sequential IDs (though starting points may differ)
        pg_ids = [u.id for u in pg_result]
        sqlite_ids = [u.id for u in sqlite_result]

        for i in range(9):
            assert pg_ids[i + 1] == pg_ids[i] + 1
            assert sqlite_ids[i + 1] == sqlite_ids[i] + 1


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