import threading
import pytest
from atomsql import Database


def test_connection_pooling_concurrency():
    """
    Verifies that the connection pool correctly handles concurrent threads
    acquiring and releasing connections.
    """
    # Initialize Database with a pool size of 3
    # Note: For standard SQLite :memory:, each connection is a separate database.
    # This test works because 'SELECT {idx}' does not rely on shared tables.
    db = Database("sqlite://:memory:", pool_size=3)

    # Verify pool initialization (starts with 1 connection by default)
    # We access the protected '_pool' only for verification in tests.
    assert db.pool._pool.qsize() == 1

    errors = []

    def worker(idx):
        try:
            # Each worker gets a connection, executes a query, and releases it.
            # The context manager inside db.execute handles the acquisition/release.
            result = db.execute(f"SELECT {idx}")
            assert result[0][0] == idx
        except Exception as e:
            errors.append(e)

    threads = []
    # Launch 10 threads (more than pool_size=3) to force contention and reuse
    for i in range(10):
        t = threading.Thread(target=worker, args=(i,))
        threads.append(t)
        t.start()

    for t in threads:
        t.join()

    # Assert no errors occurred in threads
    assert not errors, f"Errors occurred in worker threads: {errors}"

    # Clean up connections
    db.close()


def test_pool_exhaustion_logic():
    """
    Verifies that we cannot acquire more connections than the max_size
    without waiting (logic check).
    """
    import tempfile
    import os

    # Use a temporary file-based database to allow multiple connections
    with tempfile.NamedTemporaryFile(delete=False, suffix=".db") as f:
        db_path = f.name

    try:
        db = Database(f"sqlite:///{db_path}", pool_size=2)
        pool = db.pool

        # Manually check out all allowed connections
        conn1 = pool.get_connection(block=False)
        conn2 = pool.get_connection(block=False)

        # The pool should now be empty of idle connections
        assert pool._pool.empty()

        # Trying to get a third one immediately should fail or block (if we set block=False)
        # Since the pool creates on demand up to max_size, and we just pulled 2 (max),
        # creating another should not be possible if the pool logic is correct.

        with pytest.raises(Exception):  # Expecting queue.Empty or similar
            pool._pool.get(block=False)

        # Release them back
        pool.release_connection(conn1)
        pool.release_connection(conn2)

        # Close all connections before cleaning up
        pool.close_all()
        db.backend.close()
    finally:
        # Clean up the temporary database file
        try:
            if os.path.exists(db_path):
                os.unlink(db_path)
        except PermissionError:
            pass  # File might still be locked on Windows
