from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy import select, func, and_
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from datetime import datetime, timedelta

import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from config import (
    DATABASE_URL, MAX_BOTS_PER_USER,
    BOTS_CHANNEL_URL, SOURCE_CHANNEL_URL, DEVELOPER_URL, DEVELOPER_IDS
)
from information.models import (
    Base, User, Bot, BotTransfer, FactorySettings,
    ButtonSetting, MandatoryChannel, FundedChannel,
    BotGroup, BroadcastSettings, CobotUser,
    PaymentPackage, PaymentSettings, UserBalance,
    PaymentTransaction, BotSubscription,
    CobotMandatoryChannel,
    CobotFundedChannel, CobotFundedSubscription, CobotFundedPending,
)

engine = create_async_engine(
    DATABASE_URL,
    echo=False,
    pool_size=20,
    max_overflow=40,
    pool_pre_ping=True,
)

AsyncSessionFactory = async_sessionmaker(
    engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

@asynccontextmanager
async def get_session() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSessionFactory() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise

async def init_db():
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    await _init_factory_settings()
    await _init_broadcast_settings()
    await _init_button_settings()
    await _init_developers()
    await _init_payment_settings()
    await _init_payment_packages()
    print("✅ قاعدة البيانات جاهزة.")


async def _init_factory_settings():
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        if not result.scalar_one_or_none():
            session.add(FactorySettings(
                max_bots_per_user  = MAX_BOTS_PER_USER,
                bots_channel_url   = BOTS_CHANNEL_URL,
                source_channel_url = SOURCE_CHANNEL_URL,
                developer_url      = DEVELOPER_URL,
            ))


async def _init_broadcast_settings():
    async with get_session() as session:
        result = await session.execute(select(BroadcastSettings).limit(1))
        if not result.scalar_one_or_none():
            session.add(BroadcastSettings())


async def _init_button_settings():
    defaults = [
        {"button_key": "create",         "label": "إنشاء بوت",             "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "mybots",         "label": "بوتاتي",                 "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "help",           "label": "التعليمات",              "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "paid_create",    "label": "إنشاء بوت مدفوع 💎",    "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "subscription",   "label": "اشتراك 🌟",               "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bots_channel",   "label": "قناة البوتات",           "emoji": None, "color": "default", "url": BOTS_CHANNEL_URL,  "custom_emoji_id": None},
        {"button_key": "source_channel", "label": "قناة السورس",            "emoji": None, "color": "default", "url": SOURCE_CHANNEL_URL,"custom_emoji_id": None},
        {"button_key": "developer",      "label": "المطور",                 "emoji": None, "color": "default", "url": DEVELOPER_URL,     "custom_emoji_id": None},
        {"button_key": "back",           "label": "رجوع",                   "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "factory",        "label": "اصنع صانع بوتات خدمي",  "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "communication",  "label": "بوت تواصل او سايت",      "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "download",       "label": "صنع بوت تحميل",          "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "decor",          "label": "بوت زغرفه اسماء",        "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "buttons",        "label": "بوت ازرار",              "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "transcoding",    "label": "بوت تحويل صيغ",          "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "translation",    "label": "بوت ترجمه",               "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "ai",             "label": "بوت تحدث مع ذكاء اصطناعي","emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "pay_topup",      "label": "إضافة رصيد",              "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "pay_subscribe",  "label": "اشتراك مدفوع",            "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "pay_paid_bots",  "label": "البوتات المدفوعة 💎",     "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "owner_self",     "label": "👤 أنا",                  "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "owner_other",    "label": "👥 شخص آخر",              "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_transfer",   "label": "نقل البوت",               "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_edit_photo", "label": "تغيير الصورة",            "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_edit_name",  "label": "تغيير الاسم",             "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_change_type","label": "تغيير نوع البوت",         "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "transfer_accept","label": "موافقة",                  "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "transfer_reject","label": "رفض",                     "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "transfer_confirm","label": "تأكيد",                  "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "open_bot_url",   "label": "🚀 فتح",                 "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "back_to_main",   "label": "رجوع للقائمة الرئيسية", "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "confirm_token",  "label": "نعم، متابعة",            "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_url",        "label": "@username",              "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_delete",     "label": "حذف البوت",                     "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "bot_manage",     "label": "إدارة",                  "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "check_sub",      "label": "✅ تحقق من الاشتراك",    "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
        {"button_key": "sub_channel_url","label": "رابط القناة",            "emoji": None, "color": "default", "url": None,              "custom_emoji_id": None},
    ]
    async with get_session() as session:
        for btn in defaults:
            result = await session.execute(
                select(ButtonSetting).where(ButtonSetting.button_key == btn["button_key"])
            )
            if not result.scalar_one_or_none():
                session.add(ButtonSetting(**btn))


async def _init_developers():
    async with get_session() as session:
        for dev_id in DEVELOPER_IDS:
            result = await session.execute(select(User).where(User.id == dev_id))
            user = result.scalar_one_or_none()
            if not user:
                session.add(User(id=dev_id, first_name="Developer", is_dev=True))
            elif not user.is_dev:
                user.is_dev = True

async def get_or_create_user(user_id: int, username: str | None, first_name: str) -> User:
    async with get_session() as session:
        result = await session.execute(select(User).where(User.id == user_id))
        user = result.scalar_one_or_none()
        if not user:
            user = User(id=user_id, username=username, first_name=first_name)
            session.add(user)
        else:
            user.username    = username
            user.first_name  = first_name
            user.last_active = datetime.utcnow()
        return user


async def get_user(user_id: int) -> User | None:
    async with get_session() as session:
        result = await session.execute(select(User).where(User.id == user_id))
        return result.scalar_one_or_none()


async def update_last_active(user_id: int):
    async with get_session() as session:
        result = await session.execute(select(User).where(User.id == user_id))
        user = result.scalar_one_or_none()
        if user:
            user.last_active = datetime.utcnow()


async def get_all_active_users() -> list[User]:
    async with get_session() as session:
        result = await session.execute(
            select(User).where(
                User.is_banned == False,
                User.is_blocked_bot == False,
            )
        )
        return result.scalars().all()


async def get_all_users_for_open_msg() -> list[User]:
    async with get_session() as session:
        result = await session.execute(
            select(User).where(User.is_blocked_bot == False)
        )
        return result.scalars().all()

async def get_settings() -> FactorySettings:
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        return result.scalar_one()


async def update_settings(**kwargs) -> FactorySettings:
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        settings = result.scalar_one()
        for key, value in kwargs.items():
            setattr(settings, key, value)
        return settings

async def get_all_buttons() -> list[ButtonSetting]:
    async with get_session() as session:
        result = await session.execute(select(ButtonSetting))
        return result.scalars().all()


async def get_button(button_key: str) -> ButtonSetting | None:
    async with get_session() as session:
        result = await session.execute(
            select(ButtonSetting).where(ButtonSetting.button_key == button_key)
        )
        return result.scalar_one_or_none()


async def update_button(button_key: str, **kwargs):
    async with get_session() as session:
        result = await session.execute(
            select(ButtonSetting).where(ButtonSetting.button_key == button_key)
        )
        btn = result.scalar_one_or_none()
        if btn:
            for key, value in kwargs.items():
                setattr(btn, key, value)

async def get_statistics() -> dict:
    async with get_session() as session:
        now        = datetime.utcnow()
        today      = now.replace(hour=0, minute=0, second=0, microsecond=0)
        this_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        last_24h   = now - timedelta(hours=24)

        total_users = (await session.execute(
            select(func.count(User.id))
        )).scalar()

        active_24h = (await session.execute(
            select(func.count(User.id)).where(User.last_active >= last_24h)
        )).scalar()

        banned_users = (await session.execute(
            select(func.count(User.id)).where(User.is_banned == True)
        )).scalar()

        blocked_bot = (await session.execute(
            select(func.count(User.id)).where(User.is_blocked_bot == True)
        )).scalar()

        total_bots = (await session.execute(
            select(func.count(Bot.id)).where(Bot.is_active == True)
        )).scalar()

        bots_today = (await session.execute(
            select(func.count(Bot.id)).where(
                Bot.is_active == True,
                Bot.created_at >= today
            )
        )).scalar()

        bots_month = (await session.execute(
            select(func.count(Bot.id)).where(
                Bot.is_active == True,
                Bot.created_at >= this_month
            )
        )).scalar()

        cobot_users_total = (await session.execute(
            select(func.count(CobotUser.id))
        )).scalar()

        cobot_users_24h = (await session.execute(
            select(func.count(CobotUser.id)).where(CobotUser.last_active >= last_24h)
        )).scalar()

        total_devs = (await session.execute(
            select(func.count(User.id)).where(User.is_dev == True)
        )).scalar()

        total_admins = (await session.execute(
            select(func.count(User.id)).where(User.is_admin == True)
        )).scalar()

        mandatory_count = (await session.execute(
            select(func.count(MandatoryChannel.id)).where(MandatoryChannel.is_active == True)
        )).scalar()

        funded_count = (await session.execute(
            select(func.count(FundedChannel.id)).where(FundedChannel.is_active == True)
        )).scalar()

        return {
            "total_users":        total_users,
            "active_24h":         active_24h,
            "banned_users":       banned_users,
            "blocked_bot":        blocked_bot,
            "total_bots":         total_bots,
            "bots_today":         bots_today,
            "bots_month":         bots_month,
            "cobot_users_total":  cobot_users_total,
            "cobot_users_24h":    cobot_users_24h,
            "total_devs":         total_devs,
            "total_admins":       total_admins,
            "mandatory_count":    mandatory_count,
            "funded_count":       funded_count,
        }

async def get_broadcast_settings() -> BroadcastSettings:
    async with get_session() as session:
        result = await session.execute(select(BroadcastSettings).limit(1))
        return result.scalar_one()


async def update_broadcast_settings(**kwargs):
    async with get_session() as session:
        result = await session.execute(select(BroadcastSettings).limit(1))
        bs = result.scalar_one()
        for key, value in kwargs.items():
            setattr(bs, key, value)


async def get_all_bot_groups() -> list[BotGroup]:
    async with get_session() as session:
        result = await session.execute(
            select(BotGroup).where(BotGroup.is_active == True)
        )
        return result.scalars().all()

async def get_help_text() -> str:
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        s = result.scalar_one_or_none()
        if s and hasattr(s, "help_text") and s.help_text:
            return s.help_text
    return (
        "📖 <b>التعليمات</b>\n\n"
        "🔹 <b>إنشاء بوت:</b> اختر نوع البوت وأرسل توكنه من @BotFather\n"
        "🔹 <b>بوتاتي:</b> عرض وإدارة بوتاتك\n"
        "🔹 <b>نقل البوت:</b> يمكنك نقل بوتك لمستخدم آخر\n"
        "🔹 <b>تغيير الاسم/الصورة:</b> تخصيص بوتك"
    )


async def update_help_text(text: str):
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        s = result.scalar_one_or_none()
        if s:
            s.help_text = text


async def get_start_template_with_entities():
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        s = result.scalar_one_or_none()
        if not s:
            return None, None
        return s.start_template, s.start_template_entities


async def get_token_template_with_entities():
    async with get_session() as session:
        result = await session.execute(select(FactorySettings).limit(1))
        s = result.scalar_one_or_none()
        if not s:
            return None, None
        return s.token_template, s.token_template_entities


async def get_mandatory_channels() -> list[MandatoryChannel]:
    async with get_session() as session:
        result = await session.execute(
            select(MandatoryChannel).where(MandatoryChannel.is_active == True)
        )
        return result.scalars().all()


async def get_funded_channels() -> list[FundedChannel]:
    async with get_session() as session:
        result = await session.execute(
            select(FundedChannel).where(FundedChannel.is_active == True)
        )
        return result.scalars().all()


# ============================================================
# دوال نظام المدفوعات
# ============================================================

async def _init_payment_settings(factory_bot_id: int = 0):
    async with get_session() as session:
        result = await session.execute(
            select(PaymentSettings).where(PaymentSettings.factory_bot_id == factory_bot_id).limit(1)
        )
        if not result.scalar_one_or_none():
            session.add(PaymentSettings(bot_price_usd=200, factory_bot_id=factory_bot_id))


async def _init_payment_packages(factory_bot_id: int = 0):
    defaults = [
        (100,  200),
        (250,  500),
        (500,  1000),
        (750,  1500),
        (1000, 2000),
        (1500, 3000),
        (2000, 4000),
    ]
    async with get_session() as session:
        result = await session.execute(
            select(func.count(PaymentPackage.id)).where(PaymentPackage.factory_bot_id == factory_bot_id)
        )
        if result.scalar() == 0:
            for stars, usd in defaults:
                session.add(PaymentPackage(stars=stars, usd=usd, factory_bot_id=factory_bot_id))


# --- باقات الشحن ---

async def get_payment_packages(factory_bot_id: int = 0) -> list[PaymentPackage]:
    async with get_session() as session:
        result = await session.execute(
            select(PaymentPackage).where(
                PaymentPackage.is_active == True,
                PaymentPackage.factory_bot_id == factory_bot_id,
            ).order_by(PaymentPackage.stars)
        )
        return result.scalars().all()


async def get_payment_package(pkg_id: int) -> PaymentPackage | None:
    async with get_session() as session:
        result = await session.execute(
            select(PaymentPackage).where(PaymentPackage.id == pkg_id)
        )
        return result.scalar_one_or_none()


async def update_payment_package(pkg_id: int, stars: int, usd: int):
    async with get_session() as session:
        result = await session.execute(
            select(PaymentPackage).where(PaymentPackage.id == pkg_id)
        )
        pkg = result.scalar_one_or_none()
        if pkg:
            pkg.stars = stars
            pkg.usd   = usd


async def add_payment_package(stars: int, usd: int, factory_bot_id: int = 0) -> PaymentPackage:
    async with get_session() as session:
        pkg = PaymentPackage(stars=stars, usd=usd, factory_bot_id=factory_bot_id)
        session.add(pkg)
        await session.flush()
        return pkg


async def delete_payment_package(pkg_id: int):
    async with get_session() as session:
        result = await session.execute(
            select(PaymentPackage).where(PaymentPackage.id == pkg_id)
        )
        pkg = result.scalar_one_or_none()
        if pkg:
            pkg.is_active = False


# --- إعدادات المدفوعات ---

async def get_payment_settings(factory_bot_id: int = 0) -> PaymentSettings:
    async with get_session() as session:
        result = await session.execute(
            select(PaymentSettings).where(PaymentSettings.factory_bot_id == factory_bot_id).limit(1)
        )
        ps = result.scalar_one_or_none()
        if not ps:
            ps = PaymentSettings(bot_price_usd=200, factory_bot_id=factory_bot_id)
            session.add(ps)
            await session.flush()
        return ps


async def update_bot_price(price_usd_cents: int, factory_bot_id: int = 0):
    async with get_session() as session:
        result = await session.execute(
            select(PaymentSettings).where(PaymentSettings.factory_bot_id == factory_bot_id).limit(1)
        )
        ps = result.scalar_one_or_none()
        if ps:
            ps.bot_price_usd = price_usd_cents


# --- رصيد المستخدم ---

async def get_user_balance(user_id: int, factory_bot_id: int = 0) -> int:
    """يُرجع الرصيد × 100 (مثلاً 200 = 2.00$)"""
    async with get_session() as session:
        result = await session.execute(
            select(UserBalance).where(
                UserBalance.user_id == user_id,
                UserBalance.factory_bot_id == factory_bot_id,
            )
        )
        ub = result.scalar_one_or_none()
        return ub.balance if ub else 0


async def add_user_balance(user_id: int, amount_cents: int, factory_bot_id: int = 0):
    async with get_session() as session:
        result = await session.execute(
            select(UserBalance).where(
                UserBalance.user_id == user_id,
                UserBalance.factory_bot_id == factory_bot_id,
            )
        )
        ub = result.scalar_one_or_none()
        if ub:
            ub.balance += amount_cents
        else:
            session.add(UserBalance(user_id=user_id, balance=amount_cents, factory_bot_id=factory_bot_id))


async def deduct_user_balance(user_id: int, amount_cents: int, factory_bot_id: int = 0) -> bool:
    async with get_session() as session:
        result = await session.execute(
            select(UserBalance).where(
                UserBalance.user_id == user_id,
                UserBalance.factory_bot_id == factory_bot_id,
            )
        )
        ub = result.scalar_one_or_none()
        if not ub or ub.balance < amount_cents:
            return False
        ub.balance -= amount_cents
        return True


# --- معاملات الدفع ---

async def is_payment_processed(charge_id: str) -> bool:
    async with get_session() as session:
        result = await session.execute(
            select(PaymentTransaction).where(PaymentTransaction.telegram_charge_id == charge_id)
        )
        return result.scalar_one_or_none() is not None


async def record_payment(user_id: int, charge_id: str, stars: int, usd_cents: int, factory_bot_id: int = 0):
    async with get_session() as session:
        session.add(PaymentTransaction(
            user_id            = user_id,
            telegram_charge_id = charge_id,
            stars              = stars,
            usd_credited       = usd_cents,
            factory_bot_id     = factory_bot_id,
        ))


# --- اشتراكات البوتات ---

async def get_bot_subscription(bot_id: int) -> BotSubscription | None:
    async with get_session() as session:
        result = await session.execute(
            select(BotSubscription).where(
                BotSubscription.bot_id == bot_id,
                BotSubscription.is_active == True,
            )
        )
        return result.scalar_one_or_none()


async def create_bot_subscription(bot_id: int, user_id: int, factory_bot_id: int = 0) -> BotSubscription:
    from datetime import timedelta
    async with get_session() as session:
        now = datetime.utcnow()
        sub = BotSubscription(
            bot_id     = bot_id,
            user_id    = user_id,
            factory_bot_id = factory_bot_id,
            started_at = now,
            expires_at = now + timedelta(days=30),
            is_active  = True,
        )
        session.add(sub)
        # تعيين البوت كمدفوع
        result = await session.execute(select(Bot).where(Bot.id == bot_id))
        bot = result.scalar_one_or_none()
        if bot:
            bot.is_paid = True
        await session.flush()
        return sub


async def expire_bot_subscription(bot_id: int):
    async with get_session() as session:
        result = await session.execute(
            select(BotSubscription).where(BotSubscription.bot_id == bot_id)
        )
        sub = result.scalar_one_or_none()
        if sub:
            sub.is_active = False
        result2 = await session.execute(select(Bot).where(Bot.id == bot_id))
        bot = result2.scalar_one_or_none()
        if bot:
            bot.is_paid = False


async def make_bot_paid(bot_id: int, user_id: int, factory_bot_id: int = 0) -> BotSubscription:
    """يجعل البوت مدفوعاً (من قبل الإدارة) وينشئ/يجدد اشتراكاً 30 يوماً"""
    from datetime import timedelta
    async with get_session() as session:
        now = datetime.utcnow()
        result = await session.execute(
            select(BotSubscription).where(BotSubscription.bot_id == bot_id)
        )
        sub = result.scalar_one_or_none()
        if sub:
            sub.is_active  = True
            sub.started_at = now
            sub.expires_at = now + timedelta(days=30)
        else:
            sub = BotSubscription(
                bot_id=bot_id, user_id=user_id, factory_bot_id=factory_bot_id,
                started_at=now, expires_at=now + timedelta(days=30), is_active=True,
            )
            session.add(sub)
        result2 = await session.execute(select(Bot).where(Bot.id == bot_id))
        bot = result2.scalar_one_or_none()
        if bot:
            bot.is_paid = True
        await session.flush()
        return sub


async def adjust_bot_subscription(bot_id: int, days: int) -> dict:
    """يزيد (days>0) أو يقلل (days<0) مدة الاشتراك.
    يُرجع dict فيه: ok, reason, new_expires"""
    from datetime import timedelta
    async with get_session() as session:
        result = await session.execute(
            select(BotSubscription).where(BotSubscription.bot_id == bot_id)
        )
        sub = result.scalar_one_or_none()
        if not sub:
            return {"ok": False, "reason": "no_sub", "new_expires": None}
        now = datetime.utcnow()
        new_expires = sub.expires_at + timedelta(days=days)
        # التقليل لا يجوز أن يتجاوز اليوم الحالي
        if days < 0 and new_expires <= now:
            return {"ok": False, "reason": "too_much", "new_expires": None}
        sub.expires_at = new_expires
        return {"ok": True, "reason": "", "new_expires": new_expires}


async def get_user_paid_bots(user_id: int, factory_bot_id: int = 0) -> list[Bot]:
    async with get_session() as session:
        result = await session.execute(
            select(Bot).where(
                Bot.owner_id == user_id,
                Bot.is_active == True,
                Bot.is_paid == True,
                (Bot.created_by_bot_id == factory_bot_id) if factory_bot_id else (Bot.created_by_bot_id.is_(None)),
            )
        )
        return result.scalars().all()


async def get_user_unpaid_bots(user_id: int, factory_bot_id: int = 0) -> list[Bot]:
    async with get_session() as session:
        result = await session.execute(
            select(Bot).where(
                Bot.owner_id == user_id,
                Bot.is_active == True,
                Bot.is_paid == False,
                (Bot.created_by_bot_id == factory_bot_id) if factory_bot_id else (Bot.created_by_bot_id.is_(None)),
            )
        )
        return result.scalars().all()


async def is_bot_paid(bot_username: str) -> bool:
    async with get_session() as session:
        result = await session.execute(
            select(Bot).where(
                func.lower(Bot.username) == bot_username.lower(),
                Bot.is_active == True,
            )
        )
        bot = result.scalar_one_or_none()
        return bot.is_paid if bot else False


async def get_expiring_subscriptions(factory_bot_id: int = None) -> list[BotSubscription]:
    """يُرجع الاشتراكات المنتهية. factory_bot_id=None يعني كل المصانع"""
    async with get_session() as session:
        conds = [
            BotSubscription.is_active == True,
            BotSubscription.expires_at <= datetime.utcnow(),
        ]
        if factory_bot_id is not None:
            conds.append(BotSubscription.factory_bot_id == factory_bot_id)
        result = await session.execute(select(BotSubscription).where(*conds))
        return result.scalars().all()


async def get_bot_by_id(bot_id: int) -> Bot | None:
    async with get_session() as session:
        result = await session.execute(select(Bot).where(Bot.id == bot_id))
        return result.scalar_one_or_none()


async def get_factory_cobot_mandatory_channels() -> list[dict]:
    """قنوات الاشتراك الإجباري التي يفرضها المصنع الأساسي على كل البوتات المصنوعة.
    تُقرأ من جدول CobotMandatoryChannel في PostgreSQL."""
    async with get_session() as session:
        result = await session.execute(
            select(CobotMandatoryChannel).where(CobotMandatoryChannel.is_active == True)
        )
        channels = result.scalars().all()
        return [
            {
                "id":               ch.id,
                "channel_id":       ch.channel_id,
                "channel_username": ch.channel_username,
                "channel_title":    ch.channel_title,
                "invite_link":      ch.invite_link,
                "display_type":     ch.display_type.value if ch.display_type else "buttons",
            }
            for ch in channels
        ]


async def get_factory_cobot_funded_channels() -> list[dict]:
    """القنوات الممولة الإجبارية التي يفرضها المصنع الأساسي على البوتات المصنوعة.
    تُقرأ من CobotFundedChannel في PostgreSQL (النشطة وغير المكتملة فقط)."""
    async with get_session() as session:
        result = await session.execute(
            select(CobotFundedChannel).where(
                CobotFundedChannel.is_active == True,
                CobotFundedChannel.completed_at.is_(None),
            )
        )
        channels = result.scalars().all()
        return [
            {
                "id":               ch.id,
                "channel_id":       ch.channel_id,
                "channel_username": ch.channel_username,
                "channel_title":    ch.channel_title,
                "invite_link":      ch.invite_link,
                "display_type":     ch.display_type.value if ch.display_type else "buttons",
                "target_count":     ch.target_count,
                "current_count":    ch.current_count,
            }
            for ch in channels
        ]


async def record_factory_cobot_funded_sub(channel_pk: int, user_id: int, bot_id: int) -> bool:
    """يسجّل اشتراك مستخدم في قناة ممولة للمصنع الأساسي.
    يُرجع True إذا كان اشتراكاً جديداً (لم يُسجَّل من قبل)."""
    async with get_session() as session:
        exists = await session.execute(
            select(CobotFundedSubscription).where(
                CobotFundedSubscription.cobot_funded_channel_id == channel_pk,
                CobotFundedSubscription.user_id == user_id,
                CobotFundedSubscription.bot_id  == bot_id,
            )
        )
        if exists.scalar_one_or_none():
            return False
        session.add(CobotFundedSubscription(
            cobot_funded_channel_id = channel_pk,
            user_id                 = user_id,
            bot_id                  = bot_id,
        ))
        ch = await session.get(CobotFundedChannel, channel_pk)
        if ch:
            ch.current_count = (ch.current_count or 0) + 1
        return True


async def get_factory_cobot_funded_count(channel_pk: int) -> tuple[int, int]:
    """يُرجع (العدد الحالي, الهدف) لقناة ممولة."""
    async with get_session() as session:
        ch = await session.get(CobotFundedChannel, channel_pk)
        if not ch:
            return (0, 0)
        return (ch.current_count or 0, ch.target_count)


async def mark_factory_cobot_funded_complete(channel_pk: int):
    """يُعلّم القناة الممولة كمكتملة (تتوقف عن الظهور)."""
    async with get_session() as session:
        ch = await session.get(CobotFundedChannel, channel_pk)
        if ch:
            ch.is_active    = False
            ch.completed_at = datetime.utcnow()


async def mark_factory_funded_pending(channel_pk: int, user_id: int, bot_id: int):
    """يضع علامة أن هذا المستخدم طُلب منه الاشتراك في القناة الممولة (المصنع الأساسي)."""
    async with get_session() as session:
        exists = await session.execute(
            select(CobotFundedPending).where(
                CobotFundedPending.cobot_funded_channel_id == channel_pk,
                CobotFundedPending.user_id == user_id,
                CobotFundedPending.bot_id  == bot_id,
            )
        )
        if exists.scalar_one_or_none():
            return
        session.add(CobotFundedPending(
            cobot_funded_channel_id = channel_pk,
            user_id                 = user_id,
            bot_id                  = bot_id,
        ))


async def is_factory_funded_pending(channel_pk: int, user_id: int, bot_id: int) -> bool:
    """هل طُلب من هذا المستخدم الاشتراك سابقاً في هذه القناة؟"""
    async with get_session() as session:
        result = await session.execute(
            select(CobotFundedPending).where(
                CobotFundedPending.cobot_funded_channel_id == channel_pk,
                CobotFundedPending.user_id == user_id,
                CobotFundedPending.bot_id  == bot_id,
            )
        )
        return result.scalar_one_or_none() is not None


async def clear_factory_funded_pending(channel_pk: int, user_id: int, bot_id: int):
    """يزيل علامة الطلب بعد الاحتساب."""
    async with get_session() as session:
        result = await session.execute(
            select(CobotFundedPending).where(
                CobotFundedPending.cobot_funded_channel_id == channel_pk,
                CobotFundedPending.user_id == user_id,
                CobotFundedPending.bot_id  == bot_id,
            )
        )
        row = result.scalar_one_or_none()
        if row:
            await session.delete(row)


async def clear_all_factory_funded_pending(channel_pk: int):
    """يزيل كل علامات الطلب لقناة (عند اكتمالها وحذفها)."""
    async with get_session() as session:
        result = await session.execute(
            select(CobotFundedPending).where(
                CobotFundedPending.cobot_funded_channel_id == channel_pk
            )
        )
        for row in result.scalars().all():
            await session.delete(row)
