"""Repository factory tests."""

from __future__ import annotations

from collections.abc import Sequence
from typing import cast

from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from pydantic import BaseModel
from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
from tests.conftest import Book, SessionFactory

from auen import CrudRouterBuilder
from auen.repository import AsyncCrudRepository, AsyncSqlModelRepository
from auen.types import PKValue, SelectQuery


class SpyRepository:
    def __init__(self, model: type[SQLModel]) -> None:
        self.inner = AsyncSqlModelRepository(model)
        self.calls: list[str] = []

    async def get(self, session: AsyncSession, pk: PKValue) -> SQLModel | None:
        self.calls.append("get")
        return await self.inner.get(session, pk)

    async def list(
        self,
        session: AsyncSession,
        *,
        query: SelectQuery[SQLModel] | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> Sequence[SQLModel]:
        self.calls.append("list")
        return await self.inner.list(session, query=query, offset=offset, limit=limit)

    async def create(self, session: AsyncSession, *, obj_in: BaseModel) -> SQLModel:
        self.calls.append("create")
        return await self.inner.create(session, obj_in=obj_in)

    async def update(
        self,
        session: AsyncSession,
        *,
        db_obj: SQLModel,
        obj_in: BaseModel,
        partial: bool = True,
    ) -> SQLModel:
        self.calls.append("update")
        return await self.inner.update(
            session, db_obj=db_obj, obj_in=obj_in, partial=partial
        )

    async def delete(self, session: AsyncSession, *, db_obj: SQLModel) -> None:
        self.calls.append("delete")
        return await self.inner.delete(session, db_obj=db_obj)


async def test_repository_factory_used(get_session: SessionFactory) -> None:
    repo = SpyRepository(Book)

    def factory(model: type[SQLModel]) -> AsyncCrudRepository:
        assert model is Book
        return cast(AsyncCrudRepository, repo)

    app = FastAPI()
    app.include_router(
        CrudRouterBuilder.for_model(Book, get_session)
        .with_repository_factory(factory)
        .build()
    )
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        resp = await client.get("/books/")

    assert resp.status_code == 200
    assert repo.calls == ["list"]
