"""
dayong.impls
~~~~~~~~~~~~

Implementaion of interfaces and the logic for injecting them.
"""
import asyncio
from typing import Any

import tanjun
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from sqlmodel import SQLModel, select
from sqlmodel.engine.result import ScalarResult
from sqlmodel.ext.asyncio.session import AsyncSession

from dayong.configs import DayongConfig, DayongConfigLoader
from dayong.models import Message


class MessageDBImpl:
    """Implementaion of a database connection for transacting and interacting with
    message tables, those of which derive from message table models.

    The data to be selected, retrieved, and modified is determined by the table model
    object and its type. The type, in this case, is `dayong.models.Message`.
    """

    def __init__(self) -> None:
        self._conn: AsyncEngine

    async def connect(self, config: DayongConfig = tanjun.injected(type=DayongConfig)):
        """Create a database connection.

        If the `database_uri` is Falsy, the function will reattempt to get the url from
        the environment variables.

        Args:
            config (DayongConfig, optional): [description]. Defaults to
                tanjun.injected(type=DayongConfig).
        """
        loop = asyncio.get_running_loop()
        self._conn = await loop.run_in_executor(
            None,
            create_async_engine,
            config.database_uri if config.database_uri else DayongConfigLoader().load(),
        )

    async def create_table(self) -> None:
        """Create physical message tables for all the message table models stored in
        `SQLModel.metadata`.
        """
        async with self._conn.begin() as conn:
            await conn.run_sync(SQLModel.metadata.create_all)

    async def add_row(self, tabe_model_object: Message) -> None:
        """Insert a row in the message table.

        Args:
            table_model_object (Message): An instance of `dayong.models.Message` or one
            of its subclasses.
        """
        async with AsyncSession(self._conn) as session:
            loop = asyncio.get_running_loop()
            await loop.run_in_executor(None, session.add, tabe_model_object)
            await session.commit()

    async def remove_row(self, tabe_model_object: Message) -> None:
        """Delete a row in the message table.

        Args:
            table_model_object (Message): An instance of `dayong.models.Message` or one
            of its subclasses.
        """
        table_model = type(tabe_model_object)
        async with AsyncSession(self._conn) as session:
            # Temp ignore incompatible type passed to `exec()`. See:
            # https://github.com/tiangolo/sqlmodel/issues/54
            # https://github.com/tiangolo/sqlmodel/pull/58
            row: ScalarResult[Any] = await session.exec(
                select(table_model).where(  # type: ignore
                    table_model.message_id == tabe_model_object.message_id
                )
            )
            await session.delete(row)
            await session.commit()

    async def get_row(self, tabe_model_object: Message) -> ScalarResult[Any]:
        """Fetch a row from the message table.

        Args:
            tabe_model_object (Message): An instance of `dayong.models.Message` or one
            of its subclasses.

        Returns:
            ScalarResult: An `ScalarResult` object which contains a scalar value or
                sequence of scalar values.
        """
        table_model = type(tabe_model_object)
        async with AsyncSession(self._conn) as session:
            # Temp ignore incompatible type passed to `exec()`. See:
            # https://github.com/tiangolo/sqlmodel/issues/54
            # https://github.com/tiangolo/sqlmodel/pull/58
            row: ScalarResult[Any] = await session.exec(
                select(table_model).where(  # type: ignore
                    table_model.message_id == tabe_model_object.message_id
                )
            )
        return row

dayong.configs.DayongConfigLoader tanjun.injected sqlmodel.ext.asyncio.session.AsyncSession sqlmodel.select asyncio.get_running_loop
[((917, 951), 'tanjun.injected', 'tanjun.injected', ([], {'type': 'DayongConfig'}), '(type=DayongConfig)\n', (932, 951), False, 'import tanjun\n'), ((1285, 1311), 'asyncio.get_running_loop', 'asyncio.get_running_loop', ([], {}), '()\n', (1309, 1311), False, 'import asyncio\n'), ((2066, 2090), 'sqlmodel.ext.asyncio.session.AsyncSession', 'AsyncSession', (['self._conn'], {}), '(self._conn)\n', (2078, 2090), False, 'from sqlmodel.ext.asyncio.session import AsyncSession\n'), ((2122, 2148), 'asyncio.get_running_loop', 'asyncio.get_running_loop', ([], {}), '()\n', (2146, 2148), False, 'import asyncio\n'), ((2587, 2611), 'sqlmodel.ext.asyncio.session.AsyncSession', 'AsyncSession', (['self._conn'], {}), '(self._conn)\n', (2599, 2611), False, 'from sqlmodel.ext.asyncio.session import AsyncSession\n'), ((3590, 3614), 'sqlmodel.ext.asyncio.session.AsyncSession', 'AsyncSession', (['self._conn'], {}), '(self._conn)\n', (3602, 3614), False, 'from sqlmodel.ext.asyncio.session import AsyncSession\n'), ((1472, 1492), 'dayong.configs.DayongConfigLoader', 'DayongConfigLoader', ([], {}), '()\n', (1490, 1492), False, 'from dayong.configs import DayongConfig, DayongConfigLoader\n'), ((2886, 2905), 'sqlmodel.select', 'select', (['table_model'], {}), '(table_model)\n', (2892, 2905), False, 'from sqlmodel import SQLModel, select\n'), ((3889, 3908), 'sqlmodel.select', 'select', (['table_model'], {}), '(table_model)\n', (3895, 3908), False, 'from sqlmodel import SQLModel, select\n')]