from typing import Type, TypeVar, Sequence, Optional, Dict, Any

from sqlalchemy import update as update_, delete as delete_
from sqlalchemy.future import select
from db.models import ExperimentCategory, ExperimentData, ExperimentParameters, LoadParameters, RecyclingParameters

from db.postgres_db_connection import async_session_postgres

T = TypeVar("T", ExperimentCategory, ExperimentData, ExperimentParameters, LoadParameters, RecyclingParameters)


async def get_all(model_class: Type[T]) -> Sequence[T]:
    async with async_session_postgres() as session:
        result = await session.execute(select(model_class))
        return result.scalars().all()


async def get_by_id(model_class: Type[T], id: int) -> Optional[T]:
    async with async_session_postgres() as session:
        result = await session.execute(select(model_class).where(model_class.id == id))
        return result.scalar_one_or_none()


async def create(model_class: Type[T], **kwargs) -> T:
    async with async_session_postgres() as session:
        new_instance = model_class(**kwargs)
        session.add(new_instance)
        await session.commit()
        await session.refresh(new_instance)
        return new_instance


async def update(model_class: Type[T], id: int, updated_data: Dict[str, Any]) -> Optional[T]:
    async with async_session_postgres() as session:
        stmt = (
            update_(model_class)
            .where(model_class.id == id)
            .values(**updated_data)
            .execution_options(synchronize_session="fetch")
        )
        await session.execute(stmt)
        await session.commit()
        return await get_by_id(model_class, id)


async def delete(model_class: Type[T], id: int) -> bool:
    async with async_session_postgres() as session:
        stmt = delete_(model_class).where(model_class.id == id)
        result = await session.execute(stmt)
        await session.commit()
        return result.rowcount > 0