import asyncio
from typing import AsyncGenerator
from sqlalchemy import Column, Integer, String, Boolean, desc, insert, select, delete, func, update, tuple_
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.ext.asyncio import AsyncAttrs, create_async_engine, async_sessionmaker

from app.utils.path import path

engine = create_async_engine(f'sqlite+aiosqlite:///{path("database/database.db")}')
async_session = async_sessionmaker(engine, expire_on_commit=False)

class Base(AsyncAttrs, DeclarativeBase):
    pass
        
class _Users(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    start_date = Column(Integer)
    last_use = Column(Integer)
    bank = Column(String, default=None)
    wallet_trc20 = Column(String, default=None)
    wallet_ton = Column(String, default=None)
    current_balance = Column(Integer, default=0)
    all_balance = Column(Integer, default=0)
    verify = Column(Boolean, default=False)
    block = Column(Boolean, default=False)
    step = Column(String, default=None)
    
class _Admins(Base):
    __tablename__ = 'admins'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    rights = Column(String, default="[]")
    
class _Registers(Base):
    __tablename__ = 'registers'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    
class _Channels(Base):
    __tablename__ = 'channels'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    chat_id = Column(Integer)
    channel_type = Column(Integer)
    tag = Column(String)
    verify = Column(Boolean, default=False)
    
class _ChannelRequests(Base):
    __tablename__ = 'channel_requests'
    id = Column(Integer, primary_key=True)
    tag = Column(String)
    banner_tag = Column(String)
    channel_tag = Column(String)
    
class _Banners(Base):
    __tablename__ = 'banners'
    id = Column(Integer, primary_key=True)
    tag = Column(String)
    banner_name = Column(String)
    banner_limit = Column(Integer)
    banner_end = Column(Integer)
    count = Column(Integer)
    s1000 = Column(Integer)
    channel_type = Column(String)
    description = Column(String)
    msg_id = Column(Integer)
    stats_group = Column(Integer)
    date = Column(Integer)
    gifts = Column(String, default="0-0-0")
    last_send = Column(Integer)
    disable_access = Column(Boolean, default=True)
    status = Column(Integer, default=0)
    
class _Seens(Base):
    __tablename__ = 'seens'
    id = Column(Integer, primary_key=True)
    tag = Column(String)
    banner_tag = Column(String)
    user_id = Column(Integer)
    chat_id = Column(Integer)
    msg_id = Column(Integer)
    count = Column(Integer, default=0)
    all_count = Column(Integer, default=0)
    disable = Column(Boolean, default=False)
    status = Column(Integer, default=0)
    
class _Schedules(Base):
    __tablename__ = 'schedules'
    id = Column(Integer, primary_key=True)
    tag = Column(Integer)
    banner_tag = Column(Integer)
    date = Column(String)
    stype = Column(Integer)
    
class _Withdraws(Base):
    __tablename__ = 'withdraws'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    amount = Column(Integer)
    type = Column(String)
    
class _WithdrawsHistory(Base):
    __tablename__ = 'withdraws_history'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    amount = Column(Integer)
    date = Column(Integer)
    
class _Blocks(Base):
    __tablename__ = 'blocks'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    
class _Setting(Base):
    __tablename__ = 'setting'
    id = Column(Integer, primary_key=True)
    off = Column(Boolean)
    start = Column(String)
    least_withdraw = Column(Integer, default=200000)
    register = Column(String, default=".")
    banner_channel = Column(Integer, default=0)
    gifts = Column(String, default="0-0-0")
    
    
class AutoCleaningLock:
    def __init__(self):
        self._lock = asyncio.Lock()
        self._refcount = 0
        self._cleanup_callback = None

    def set_cleanup(self, callback):
        self._cleanup_callback = callback

    async def __aenter__(self):
        self._refcount += 1
        await self._lock.acquire()
        return self

    async def __aexit__(self, exc_type, exc, tb):
        self._lock.release()
        self._refcount -= 1
        if self._refcount == 0 and self._cleanup_callback:
            self._cleanup_callback()

seens_locks = {}
def get_seen_lock(user_id):
    lock = seens_locks.get(user_id)

    if lock is None:
        lock = AutoCleaningLock()
        lock.set_cleanup(lambda: seens_locks.pop(user_id, None))
        seens_locks[user_id] = lock
        
    return lock

channel_locks = {}
def get_channel_lock(user_id):
    lock = channel_locks.get(user_id)

    if lock is None:
        lock = AutoCleaningLock()
        lock.set_cleanup(lambda: channel_locks.pop(user_id, None))
        channel_locks[user_id] = lock
        
    return lock

class Initialize:
    async def initialize(self):
        async with engine.begin() as conn:
            await conn.run_sync(Base.metadata.create_all)
            
class Users:
    async def add_user(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Users).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_user(self, **kwargs) -> _Users:
        async with async_session() as session:
            statement = select(_Users).filter_by(**kwargs)
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_users(self) -> AsyncGenerator[_Users, None]:
        async with async_session() as session:
            statement = select(_Users)
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
                
    async def get_balance_users(self) -> AsyncGenerator[_Users, None]:
        async with async_session() as session:
            statement = select(_Users).order_by(_Users.current_balance.desc()).limit(10)
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
                
    async def get_users_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Users)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Users, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_last_users(self, timestamp: int) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Users).where(_Users.start_date >= timestamp)
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_last_use_users(self, timestamp: int) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Users).where(_Users.last_use >= timestamp)
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_users_all_balance(self) -> int:
        async with async_session() as session:
            statement = select(func.sum(_Users.all_balance)).select_from(_Users)
            result = await session.execute(statement)
            return result.scalar()
            
    async def get_users_current_balance(self) -> int:
        async with async_session() as session:
            statement = select(func.sum(_Users.current_balance)).select_from(_Users)
            result = await session.execute(statement)
            return result.scalar()
        
    async def update_user(self, user: _Users, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Users).where(_Users.id == user.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            user.__dict__.update(kwargs)

class Banners:
    async def add_banner(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Banners).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_banners(self, **kwargs) -> AsyncGenerator[_Banners, None]:
        async with async_session() as session:
            statement = select(_Banners)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Banners, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
    
    async def get_banners_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Banners)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Banners, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_banner(self, **kwargs) -> _Banners:
        async with async_session() as session:
            statement = select(_Banners)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Banners, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def delete_banner(self, id: int) -> None:
        async with async_session() as session:
            statement = delete(_Banners).where(_Banners.id == id)
            await session.execute(statement)
            await session.commit()
    
    async def update_expired_banner(self, timestamp: int) -> None:
        async with async_session() as session:
            statement = update(_Banners).where(_Banners.status == 0).where(_Banners.banner_end == 2).where(timestamp - _Banners.date >= _Banners.count * 86400).values(status=1)
            await session.execute(statement)
            await session.commit()
    
    async def update_banner(self, banner: _Banners, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Banners).where(_Banners.id == banner.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            banner.__dict__.update(kwargs)
            
class Seens:
    async def add_seen(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Seens).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_seens(self, **kwargs) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            statement = select(_Seens)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
        
    async def get_duplicates_seens(self) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            subq = (
                select(_Seens.chat_id, _Seens.banner_tag)
                .group_by(_Seens.chat_id, _Seens.banner_tag)
                .having(func.count() > 1)
                .subquery()
            )

            statement = (
                select(_Seens)
                .where(_Seens.status == 0)
                .where(
                    tuple_(_Seens.chat_id, _Seens.banner_tag).in_(
                        select(subq.c.chat_id, subq.c.banner_tag)
                    )
                )
            )

            result = await session.execute(statement)

            for row in result:
                yield row[0]
                
    async def get_group_seens(self, **kwargs) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            statement = select(_Seens).group_by(_Seens.banner_tag)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
        
    async def get_group_seens2(self, **kwargs) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            statement = select(_Seens).group_by(_Seens.user_id)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
                
    async def get_seens_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Seens)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()

    async def get_all_seens_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.sum(_Seens.all_count + _Seens.count)).select_from(_Seens)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar() or 0
        
    async def get_seen(self, **kwargs) -> _Seens:
        async with async_session() as session:
            statement = select(_Seens)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Seens, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_ranks(self) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            statement = select(_Seens.user_id, func.sum(_Seens.all_count + _Seens.count).label("total_count")).group_by(_Seens.user_id).order_by(func.sum(_Seens.all_count + _Seens.count).desc())
            result = await session.execute(statement)

            for _ in result:
                yield _
    
    async def get_banner_ranks(self, banner_tag: str) -> AsyncGenerator[_Seens, None]:
        async with async_session() as session:
            statement = select(_Seens.user_id, func.sum(_Seens.all_count + _Seens.count).label("total_count")).where(_Seens.banner_tag == banner_tag).where(_Seens.status == 0).group_by(_Seens.user_id).order_by(func.sum(_Seens.all_count + _Seens.count).desc())
            result = await session.execute(statement)

            for _ in result:
                yield _
                
    async def get_rank(self, banner_tag: str, user_id: int):
        async with async_session() as session:
            statement = select(_Seens.user_id).where(_Seens.banner_tag == banner_tag).where(_Seens.status == 0).group_by(_Seens.user_id).order_by(func.sum(_Seens.all_count + _Seens.count).desc())
            result = await session.execute(statement)

            rank = 1

            for _ in result:
                if _[0] == user_id:
                    break

                rank += 1

            return  rank

    async def update_seen(self, seen: _Seens, **kwargs) -> None:
        lock = get_seen_lock(seen.tag)
        async with lock:
            async with async_session() as session:
                statement = update(_Seens).where(_Seens.id == seen.id).values(kwargs)
                await session.execute(statement)
                await session.commit()
                seen.__dict__.update(kwargs)

    async def update_all_seens(self, user_id: int, banner_tag: str, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Seens).where(_Seens.user_id == user_id).where(_Seens.banner_tag == banner_tag).values(kwargs)
            await session.execute(statement)
            await session.commit()
            
    async def delete_seen(self, id: int) -> None:
        async with async_session() as session:
            statement = delete(_Seens).where(_Seens.id == id)
            await session.execute(statement)
            await session.commit()
            
    async def delete_seens(self, tag: str) -> None:
        async with async_session() as session:
            statement = delete(_Seens).where(_Seens.tag == tag)
            await session.execute(statement)
            await session.commit()

class Schedules:
    async def add_schedule(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Schedules).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_schedules(self, **kwargs) -> AsyncGenerator[_Schedules, None]:
        async with async_session() as session:
            statement = select(_Schedules)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Schedules, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
    
    async def get_schedules_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Schedules)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Schedules, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_schedule(self, **kwargs) -> _Schedules:
        async with async_session() as session:
            statement = select(_Schedules)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Schedules, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def delete_schedule(self, tag: str) -> None:
        async with async_session() as session:
            statement = delete(_Schedules).where(_Schedules.tag == tag)
            await session.execute(statement)
            await session.commit()
    
    async def delete_schedules(self, banner_tag: int) -> None:
        async with async_session() as session:
            statement = delete(_Schedules).where(_Schedules.banner_tag == banner_tag)
            await session.execute(statement)
            await session.commit()
    
    async def update_schedule(self, schedule: _Schedules, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Schedules).where(_Schedules.id == schedule.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            schedule.__dict__.update(kwargs)
            
class Admins:
    async def add_admin(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Admins).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_admins(self, **kwargs) -> AsyncGenerator[_Admins, None]:
        async with async_session() as session:
            statement = select(_Admins)

            for column, value in kwargs.items():
                column_attr = getattr(_Admins, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            
            for _ in result:
                yield _[0]
    
    async def get_admins_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Admins)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Admins, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_admin(self, **kwargs) -> _Admins:
        async with async_session() as session:
            statement = select(_Admins)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Admins, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def delete_admin(self, id: int) -> None:
        async with async_session() as session:
            statement = delete(_Admins).where(_Admins.id == id)
            await session.execute(statement)
            await session.commit()

    async def update_admin(self, admin_: _Admins, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Admins).where(_Admins.id == admin_.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            admin_.__dict__.update(kwargs)
            
class Registers:
    async def add_register(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Registers).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_register(self, **kwargs) -> _Registers:
        async with async_session() as session:
            statement = select(_Registers)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Registers, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def delete_register(self, user_id: int) -> None:
        async with async_session() as session:
            statement = delete(_Registers).where(_Registers.user_id == user_id)
            await session.execute(statement)
            await session.commit()
            
class Channels:
    async def add_channel(self, **kwargs) -> None:
        lock = get_channel_lock(kwargs.get("chat_id", 0))
        async with lock:
            async with async_session() as session:
                statement = insert(_Channels).values(**kwargs)
                await session.execute(statement)
                await session.commit()
        
    async def get_channels(self, **kwargs) -> AsyncGenerator[_Channels, None]:
        async with async_session() as session:
            statement = select(_Channels).filter_by(**kwargs)
            result = await session.execute(statement)
            
            for _ in result:
                yield _[0]
    
    async def get_duplicates_channels(self) -> AsyncGenerator[_Channels, None]:
        async with async_session() as session:
            subq = (
                select(_Channels.chat_id)
                .group_by(_Channels.chat_id)
                .having(func.count() > 1)
                .subquery()
            )

            statement = select(_Channels).where(
                _Channels.chat_id.in_(select(subq.c.chat_id))
            )

            result = await session.execute(statement)

            for row in result:
                yield row[0]

    async def get_channels_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Channels)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Channels, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
        
    async def get_channel(self, **kwargs) -> _Channels:
        async with async_session() as session:
            statement = select(_Channels)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Channels, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def delete_channel(self, tag: str) -> None:
        async with async_session() as session:
            statement = delete(_Channels).where(_Channels.tag == tag)
            await session.execute(statement)
            await session.commit()

    async def update_channel(self, channel_: _Channels, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Channels).where(_Channels.id == channel_.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            channel_.__dict__.update(kwargs)
            
class ChannelRequests:
    async def add_channel_request(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_ChannelRequests).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_channel_request(self, **kwargs) -> _ChannelRequests:
        async with async_session() as session:
            statement = select(_ChannelRequests)
            
            for column, value in kwargs.items():
                column_attr = getattr(_ChannelRequests, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_channel_requests(self, **kwargs) -> AsyncGenerator[_ChannelRequests, None]:
        async with async_session() as session:
            statement = select(_ChannelRequests)

            for column, value in kwargs.items():
                column_attr = getattr(_ChannelRequests, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            
            for _ in result:
                yield _[0]
    
    async def get_channel_requests_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_ChannelRequests)
            
            for column, value in kwargs.items():
                column_attr = getattr(_ChannelRequests, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
        
    async def delete_channel_request(self, id: int) -> None:
        async with async_session() as session:
            statement = delete(_ChannelRequests).where(_ChannelRequests.id == id)
            await session.execute(statement)
            await session.commit()
            
class Withdraws:
    async def add_withdraw(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Withdraws).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_withdraw(self, **kwargs) -> _Withdraws:
        async with async_session() as session:
            statement = select(_Withdraws)
            
            for column, value in kwargs.items():
                column_attr = getattr(_Withdraws, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_withdraws(self) -> AsyncGenerator[_Withdraws, None]:
        async with async_session() as session:
            statement = select(_Withdraws)
            result = await session.execute(statement)
            
            for _ in result:
                yield _[0]
    
    async def get_withdraw_count(self) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Withdraws)
            result = await session.execute(statement)
            return result.scalar()
        
    async def delete_withdraw(self, id: int) -> None:
        async with async_session() as session:
            statement = delete(_Withdraws).where(_Withdraws.id == id)
            await session.execute(statement)
            await session.commit()
             
class WithdrawsHistory:
    async def add_withdraw_history(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_WithdrawsHistory).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_withdraw_histories(self, **kwargs) -> AsyncGenerator[_WithdrawsHistory, None]:
        async with async_session() as session:
            statement = select(_WithdrawsHistory)
            
            for column, value in kwargs.items():
                column_attr = getattr(_WithdrawsHistory, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            
            for _ in result:
                yield _[0]
    
    async def get_withdraw_histories_count(self, **kwargs) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_WithdrawsHistory)
            
            for column, value in kwargs.items():
                column_attr = getattr(_WithdrawsHistory, column, None)
                if column_attr is not None:
                    statement = statement.where(column_attr == value)
                    
            result = await session.execute(statement)
            return result.scalar()

class Blocks:
    async def add_block(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Blocks).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_blocks(self) -> AsyncGenerator[_Blocks, None]:
        async with async_session() as session:
            statement = select(_Blocks)
            result = await session.execute(statement)

            for _ in result:
                yield _[0]
    
    async def get_block(self, user_id: str) -> _Blocks:
        async with async_session() as session:
            statement = select(_Blocks).where(_Blocks.user_id == user_id)
            result = await session.execute(statement)
            return result.scalar()
    
    async def get_blocks_count(self) -> int:
        async with async_session() as session:
            statement = select(func.count()).select_from(_Blocks)
            result = await session.execute(statement)
            return result.scalar()
        
    async def delete_block(self, user_id: str) -> None:
        async with async_session() as session:
            statement = delete(_Blocks).where(_Blocks.user_id == user_id)
            await session.execute(statement)
            await session.commit()

class Setting:
    async def add_setting(self, **kwargs) -> None:
        async with async_session() as session:
            statement = insert(_Setting).values(**kwargs)
            await session.execute(statement)
            await session.commit()
        
    async def get_setting(self) -> _Setting:
        async with async_session() as session:
            statement = select(_Setting)
            result = await session.execute(statement)
            return result.scalar()
    
    async def update_setting(self, setting: _Setting, **kwargs) -> None:
        async with async_session() as session:
            statement = update(_Setting).where(_Setting.id == setting.id).values(kwargs)
            await session.execute(statement)
            await session.commit()
            setting.__dict__.update(kwargs)
            
class Database(
    Initialize,
    Users,
    Registers,
    Banners,
    Seens,
    Schedules,
    Admins,
    Channels,
    ChannelRequests,
    Withdraws,
    WithdrawsHistory,
    Blocks,
    Setting
):
    pass