from sqlalchemy import delete
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.exc import InvalidRequestError
from sqlalchemy.pool import NullPool
import config
from utils import logger


DATABASE_URL = f"postgresql+asyncpg://{config.DB_USER}:{config.DB_PASSWORD}@{config.DB_HOST}:{config.DB_PORT}/{config.DB_NAME}"

engine = create_async_engine(DATABASE_URL, poolclass=NullPool)
SessionLocal = async_sessionmaker(engine)

Base = declarative_base()


class CRUD:

    @staticmethod
    async def create(db_data, refresh: bool = False):
        try:
            is_lst = isinstance(db_data, list)
            async with SessionLocal() as db:
                if is_lst:
                    logger.debug(f"Создаю {len(db_data)} записей")
                    try:
                        db.add_all(db_data)
                    except InvalidRequestError:
                        for data in db_data:
                            await db.merge(data)
                else:
                    logger.debug("Создаю запись")
                    db.add(db_data)
                await db.commit()
                if refresh:
                    if is_lst:
                        logger.debug(f"Обновляю {len(db_data)} записей")
                        for data in db_data:
                            await db.refresh(data)
                    else:
                        logger.debug("Обновляю запись")
                        await db.refresh(db_data)
                logger.debug("Запись создана")
                return db_data if refresh else None
        except Exception as e:
            logger.error(f"Ошибка создания: {str(e)}", exc_info=True)
            return None

    @staticmethod
    async def read(query, all: bool = False):
        try:
            async with SessionLocal() as db:
                logger.debug(f"Чтение записей. Все: {all}")
                results = await db.execute(query)
                logger.debug(f"Чтение завершено")
                return (
                    results.unique().scalars().all()
                    if all
                    else results.unique().scalars().first()
                )
        except Exception as e:
            logger.error(f"Ошибка чтения: {str(e)}", exc_info=True)
            return None

    @staticmethod
    async def delete(db_data) -> bool:
        def itemdebug(instance):
            from sqlalchemy import inspect

            state = inspect(instance)

            if state.identity is None:
                pKey = None
                pValue = None
            else:
                mapper = state.mapper
                pKey = mapper.primary_key[0].name
                pValue = getattr(instance, pKey)

            return {"key": pKey, "value": pValue, "class": instance.__class__}

        async def deleteFromDB(data, db):
            itemData = itemdebug(data)
            query = delete(itemData["class"]).where(
                getattr(itemData["class"], itemData["key"]) == itemData["value"]
            )
            await db.execute(query)

        async with SessionLocal() as db:
            try:
                if isinstance(db_data, list):
                    logger.debug(f"Удаляю записей: {len(db_data)}")
                    for data in db_data:
                        await deleteFromDB(data, db)
                else:
                    logger.debug("Удаляю запись")
                    await deleteFromDB(db_data, db)
                await db.commit()
                logger.debug("Запись удалена")
                return True
            except Exception as e:
                await db.rollback()
                logger.error(f"Ошибка удаления: {str(e)}", exc_info=True)
                return False

    @staticmethod
    async def update(model, id: int, **kwargs):
        from sqlalchemy import update as sa_update

        async with SessionLocal() as db:
            try:
                query = (
                    sa_update(model)
                    .where(model.id == id)
                    .values(**kwargs)
                    .execution_options(synchronize_session="fetch")
                )
                await db.execute(query)
                await db.commit()

                logger.debug("Запись обновлена")

                return await db.get(model, id)

            except Exception as e:
                await db.rollback()
                logger.error(f"Ошибка обновления: {str(e)}", exc_info=True)
                return None
