from collections.abc import AsyncIterable

from aioboto3.session import Session
from dishka import Provider, Scope, provide
from httpx import AsyncClient, Timeout
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine

from template_project.adapters.ml_api_gateway import MlApiGateway
from template_project.adapters.s3_storage import AioBoto3ClientLike
from template_project.web_api.configuration import DatabaseConfiguration, MlApiConfiguration, S3Config


class ConnectionProvider(Provider):
    @provide(scope=Scope.APP)
    async def engine(self, configuration: DatabaseConfiguration) -> AsyncIterable[AsyncEngine]:
        engine = create_async_engine(configuration.url.get_value())
        yield engine
        await engine.dispose()

    @provide(scope=Scope.REQUEST)
    async def async_session(self, engine: AsyncEngine) -> AsyncIterable[AsyncSession]:
        session = AsyncSession(
            bind=engine,
            expire_on_commit=False,
        )
        async with session:
            yield session

    @provide(scope=Scope.APP)
    async def s3_client(self, config: S3Config) -> AsyncIterable[AioBoto3ClientLike]:
        session = Session()  # type: ignore[no-untyped-call]

        async with session.client(
            "s3",
            endpoint_url=config.endpoint_url,
            aws_access_key_id=config.access_key,
            aws_secret_access_key=config.secret_key,
        ) as s3_client:
            yield s3_client

    @provide(scope=Scope.APP)
    async def ml_api_gateway(self, config: MlApiConfiguration) -> AsyncIterable[MlApiGateway]:
        timeout = Timeout(30.0, read=30.0)
        async with AsyncClient(base_url=config.url, timeout=timeout) as client:
            yield MlApiGateway(client)
