from collections.abc import AsyncGenerator, Generator
from datetime import datetime, timezone

from httpx import ASGITransport, AsyncClient
import pytest
import pytest_asyncio
from sqlalchemy import event
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker

# Re-use models from top-level conftest
from tests.conftest import Post, User
from tests.integration.ariadne_app import create_app


@pytest_asyncio.fixture
async def integration_seeded(async_engine: AsyncEngine) -> list[User]:
    factory = async_sessionmaker(async_engine, expire_on_commit=False)
    start = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
    users = [
        User(
            id=i,
            name=f"User {i:02d}",
            created_at=start.replace(hour=i % 24),
        )
        for i in range(1, 21)
    ]
    posts = [
        Post(
            id=((user_id - 1) * 3) + offset,
            user_id=user_id,
            title=f"Post {((user_id - 1) * 3) + offset:03d}",
        )
        for user_id in range(1, 21)
        for offset in range(1, 4)
    ]
    async with factory() as session:
        session.add_all(users)
        await session.commit()
    async with factory() as session:
        session.add_all(posts)
        await session.commit()
    return users


@pytest_asyncio.fixture
async def client(async_engine: AsyncEngine, integration_seeded: list[User]) -> AsyncGenerator[AsyncClient]:
    factory = async_sessionmaker(async_engine, expire_on_commit=False)
    app = create_app(factory)
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test", follow_redirects=True) as c:
        yield c


@pytest.fixture
def query_counter(async_engine: AsyncEngine) -> Generator[dict[str, int]]:
    counts: dict[str, int] = {"n": 0}

    def before_exec(conn, cursor, statement, parameters, context, executemany):
        stmt = statement.strip().upper()
        if stmt.startswith(("SELECT", "INSERT", "UPDATE", "DELETE")):
            counts["n"] += 1

    event.listen(async_engine.sync_engine, "before_cursor_execute", before_exec)
    yield counts
    event.remove(async_engine.sync_engine, "before_cursor_execute", before_exec)
