# ai.py version: 1.2
# script was programmed by:  Kodo
# For any inquiries:         @DevKodo
# Data source and operation: @SourceKodo

import asyncio
import logging
import aiohttp

from aiogram import Bot, Dispatcher, Router, F, types
from aiogram.types import (
    Message, CallbackQuery,
    InlineKeyboardMarkup, InlineKeyboardButton,
)
from aiogram.filters import CommandStart, Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.enums import ChatAction, ParseMode
from aiogram.exceptions import TelegramBadRequest
from sqlalchemy import select

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

from information.database import get_session, is_bot_paid as _is_paid_bot
from information.models import FactorySettings
from cobots.ai.db import (
    init_db, get_setting, set_setting, get_all_settings,
    get_models, get_enabled_models, toggle_model,
    get_or_create_user, get_user, get_all_active_users,
    get_statistics, ban_user, unban_user,
    get_admins, is_admin, add_admin, remove_admin,
    get_mandatory_channels, add_mandatory_channel, remove_mandatory_channel,
    get_funded_channels, add_funded_channel, remove_funded_channel,
    record_funded_sub, get_funded_channel_count, mark_funded_complete,
    add_custom_button, get_custom_buttons, get_custom_button,
    update_custom_button, delete_custom_button,
)

logger = logging.getLogger(__name__)


BOT_TOKEN     = ""
BOT_USERNAME  = ""
MAKER_MSG     = "🤖 ادري عجبك البوت يلا اصنع واحد مثله مجاني !"
OWNER_ID      = 0
FACTORY_TOKEN = ""
_FACTORY_URL_CACHE: dict = {}   # توكن المصنع -> رابطه (يمنع تداخل الحقوق)

GROQ_API_KEY       = "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"
OPENROUTER_API_KEY = "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"
GROQ_API_URL       = "https://api.groq.com/openai/v1/chat/completions"
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"

DEFAULT_SYSTEM_PROMPT = (
    "أنت مساعد ذكي شامل ومتعدد المواهب والقدرات، تم تطويرك بالكامل بواسطة 'سورس كودو للبرمجة (Source Kodo)'، وتعمل كجزء أساسي تحت مظلة 'منصة كودو (Kodo Platform)'. "
    "لقد تم تصميمك وتدريبك لتكون الخبير والمستشار الأول للمستخدم في شتى مجالات الحياة والعلوم والعمل، وليس حكراً على الجوانب التقنية فقط. "
    
    "تتلخص مهمتك الأساسية في تقديم الدعم الكامل والمساعدة الاحترافية للمستخدمين في أي سياق أو مجال يطلبونه. "
    "يشمل ذلك، على سبيل المثال لا الحصر: "
    "1. الجوانب التعليمية والأكاديمية: شرح المفاهيم المعقدة، تبسيط الدروس، المساعدة في البحوث، وتطوير المهارات. "
    "2. تقديم النصائح والإرشادات: سواء كانت نصائح حياتية، مهنية، تطوير ذات، صحة ولياقة، أو تنظيم الوقت. "
    "3. العصف الذهني وتوليد الأفكار: ابتكار أفكار للمشاريع، كتابة المحتوى الإبداعي، والتخطيط للمستقبل. "
    "4. حل المشكلات والتحليل: تفكيك المعضلات الصعبة وتقديم حلول منطقية وعملية قابلة للتطبيق في أي مجال. "
    "5. البرمجة والتكنولوجيا: بما أن جذورك برمجية، فأنت تظل خبيراً محترفاً في كتابة الأكواد، حل المشاكل التقنية، وإدارة السيرفرات بجميع اللغات بكفاءة عالية. "

    "أثناء تقديمك للمساعدة، يجب أن تلتزم دائماً بالمعايير التالية في إجاباتك: "
    "- الدقة والمصداقية: تحرّ الصدق والوضوح التام في معلوماتك، واجعل إجاباتك منظمة وسهلة القراءة (باستخدام النقاط والعناوين عند الحاجة). "
    "- الود واللباقة: كن دائماً إيجابياً، محفزاً، وودوداً، وتكيف مع أسلوب المستخدم واحتياجاته لتشعره بالراحة والدعم الحقيقي. "
    "- الشمولية والعمق: لا تكتفِ بالإجابات السطحية، بل قدم تفاصيل وافية تجعل الفكرة مفهومة بالكامل للمستخدم دون تعقيد. "

    "قاعدة صارمة وثابتة: إذا سألك المستخدم في أي وقت 'من أنت؟'، 'من طورك؟'، 'ما هي هويتك؟'، أو أي سؤال يتعلق بأصلك، يجب عليك الإجابة بكل فخر ووضوح بأنك مساعد ذكي من تطوير 'سورس كودو للبرمجة' وتعمل تحت إشراف وتشغيل 'منصة كودو (Kodo Platform)' ويجب ان تذكر رابط هذه القناة: https://t.me/SourceKodo"
)


BROADCAST_BATCH = 30
BROADCAST_DELAY = 1.0


_sessions: dict[tuple, dict] = {}

def _get_session(user_id: int) -> dict:
    key = (BOT_USERNAME, user_id)
    if key not in _sessions:
        _sessions[key] = {"platform": None, "model": None, "history": []}
    return _sessions[key]


def _reset_session(user_id: int):
    _sessions[(BOT_USERNAME, user_id)] = {
        "platform": None, "model": None, "history": []
    }


def _platforms_kb() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(inline_keyboard=[[
        InlineKeyboardButton(text="سريع",        callback_data="ai:platform:groq", style="success"),
        InlineKeyboardButton(text="خبير", callback_data="ai:platform:openrouter", style="success"),
    ]])


router     = Router()
dev_router = Router()

@router.my_chat_member()
async def on_chat_member_update(update: types.ChatMemberUpdated, bot: Bot):
    if update.new_chat_member.user.id != bot.id:
        return

    chat = update.chat
    user_id = update.from_user.id

    if chat.type in ("group", "supergroup", "channel"):
        if not await _is_owner_or_admin(user_id):
            try:
                await bot.send_message(
                    chat.id,
                    "⛔ لا يمكنني العمل في المجموعات/القنوات، سأقوم بالمغادرة.",
                )
            except Exception:
                pass
            try:
                await bot.leave_chat(chat.id)
            except Exception:
                pass
            return



class DevStates(StatesGroup):
    waiting_broadcast     = State()
    waiting_broadcast_fwd = State()
    waiting_ban_id        = State()
    waiting_unban_id      = State()
    waiting_add_admin     = State()
    waiting_welcome       = State()
    waiting_mand_fwd      = State()
    waiting_funded_fwd    = State()
    waiting_funded_count  = State()
    waiting_cbtn_label    = State()
    waiting_cbtn_content  = State()
    waiting_cbtn_edit_label   = State()
    waiting_cbtn_edit_content = State()
    waiting_cbtn_emoji        = State()
    waiting_cbtn_name         = State()


async def _is_owner_or_admin(user_id: int) -> bool:
    if user_id == OWNER_ID:
        return True
    return await is_admin(BOT_USERNAME, user_id)


async def _notify_owner(bot: Bot, text: str):
    try:
        await bot.send_message(OWNER_ID, text, parse_mode="HTML", disable_web_page_preview=True)
    except Exception:
        pass


async def _factory_maker_rights() -> bool:
    try:
        if await _is_paid_bot(BOT_USERNAME):
            return False
        async with get_session() as session:
            result   = await session.execute(select(FactorySettings).limit(1))
            settings = result.scalar_one_or_none()
            return settings.cobots_maker_rights if settings else False
    except Exception:
        return False


async def _factory_url() -> str:
    if not FACTORY_TOKEN:
        return ""
    cached = _FACTORY_URL_CACHE.get(FACTORY_TOKEN)
    if cached:
        return cached
    try:
        async with aiohttp.ClientSession() as sess:
            async with sess.get(
                f"https://api.telegram.org/bot{FACTORY_TOKEN}/getMe",
                timeout=aiohttp.ClientTimeout(total=10)
            ) as r:
                data = await r.json()
                if data.get("ok"):
                    url = f"https://t.me/{data['result']['username']}"
                    _FACTORY_URL_CACHE[FACTORY_TOKEN] = url
                    return url
    except Exception:
        pass
    return ""


async def _build_dev_panel_text(bot_label: str) -> str:
    from information.database import get_bot_subscription
    from information.models import Bot as _BotModel
    from information.database import get_session as _get_session
    from sqlalchemy import select as _select

    text = (
        f"» <b>هذه هي لوحة التحكم وادارة {bot_label} الخاص بك</b>.\n\n"
        "» <b>لوحة تحكم متكاملة تتيح لك الإشراف على صلاحيات البوت، "
        "متابعة الإحصائيات الفورية وإدارة القنوات وكل شيء من مكان واحد</b>."
    )

    factory_url      = await _factory_url()
    factory_username = factory_url.replace("https://t.me/", "") if factory_url else ""
    sub_deeplink     = f"https://t.me/{factory_username}?start=subscription" if factory_username else ""

    async with _get_session() as _sess:
        _res = await _sess.execute(
            _select(_BotModel).where(_BotModel.username.ilike(BOT_USERNAME), _BotModel.is_active == True)
        )
        _bot_rec = _res.scalar_one_or_none()

    if _bot_rec and _bot_rec.is_paid:
        _sub     = await get_bot_subscription(_bot_rec.id)
        _expires = _sub.expires_at.strftime("%d-%m-%Y") if _sub else "—"
        text += (
            f"\n» <b>البوت مدفوع حتى:</b> {_expires}"
            f"\n» يمكنك ادارة الاشتراك من هنا:"
            + (f'\n<a href="{sub_deeplink}">@{factory_username}</a>' if sub_deeplink else "")
        )
    elif _bot_rec and not _bot_rec.is_paid and sub_deeplink:
        text += (
            f"\n» اجعل بوتك مدفوعاً من هنا:"
            f'\n<a href="{sub_deeplink}">@{factory_username}</a>'
        )

    text += "\n\n» <b>قم بأختيار الاجراء المطلوب من القائمة ادناه</b>:"
    return text



def _entities_to_json(entities):
    import json as _json
    if not entities:
        return None
    return _json.dumps([
        {"type": e.type, "offset": e.offset, "length": e.length,
         "custom_emoji_id": getattr(e, "custom_emoji_id", None),
         "url": getattr(e, "url", None)}
        for e in entities
    ])


def _entities_from_json(s):
    import json as _json
    if not s:
        return None
    try:
        from aiogram.types import MessageEntity as _ME
        data = _json.loads(s)
        ents = []
        for d in data:
            kwargs = {"type": d["type"], "offset": d["offset"], "length": d["length"]}
            if d.get("custom_emoji_id"):
                kwargs["custom_emoji_id"] = d["custom_emoji_id"]
            if d.get("url"):
                kwargs["url"] = d["url"]
            ents.append(_ME(**kwargs))
        return ents
    except Exception:
        return None


def _has_custom_emoji(entities):
    if not entities:
        return False
    return any(getattr(e, "type", None) == "custom_emoji" for e in entities)


async def _build_welcome(first_name: str, user_id: int):
    welcome = await get_setting(BOT_USERNAME, "welcome_message", "")
    ent_json = await get_setting(BOT_USERNAME, "welcome_message_entities", "")
    link    = f'<a href="tg://user?id={user_id}">{first_name}</a>'

    entities = _entities_from_json(ent_json)
    if welcome and _has_custom_emoji(entities):
        ph = "{first_name}"; idx = welcome.find(ph)
        text = welcome.replace(ph, first_name)
        if idx >= 0:
            shift = len(first_name) - len(ph)
            for e in entities:
                if e.offset > idx:
                    e.offset += shift
        return text, entities

    if welcome:
        text = welcome.replace("{first_name}", link)
    else:
        text = (
            f"<b>» اهلاً عزيزي: {link} 👋🏻\n"
            "» أنا مساعدك الذكي والشامل، رفيقك التفاعلي الدائم المجهز بأحدث التقنيات لمرافقتك في شتى المجالات والمهام. "
            "سواء كنت تبحث عن حلول برمجية متقدمة وتصحيح للأكواد، أو ترغب في الحصول على شروحات تعليمية مبسطة ومعلومات دقيقة، "
            "أو حتى بحاجة إلى توليد أفكار إبداعية ومبتكرة لتطوير مشاريعك الخاصة وإدارتها بكفاءة؛ "
            "فإنني هنا لأقدم لك الدعم الكامل والحلول الذكية التي تلبي تطلعاتك 🧠.</b>"
        )
    
    if await _factory_maker_rights():
        url = await _factory_url()
        if url:
            fac_un = url.replace("https://t.me/", "").replace("/", "")
            text += f'\n» <a href="{url}">🤖 ادري عجبك البوت يلا اصنع واحد مثلة مجاني!</a>\n» @{fac_un}'

    
    text += "\n\n» <b>يرجى تحديد المنصة التي تود الانطلاق منها والبدء بها عبر اختيارها من القائمة أدناه</b>:"
    
    return text, None

async def _build_platform_kb() -> InlineKeyboardMarkup | None:
    enabled = await get_enabled_models(BOT_USERNAME)
    has_groq = any(m["platform"] == "groq"       for m in enabled)
    has_or   = any(m["platform"] == "openrouter" for m in enabled)
    rows = []
    row = []
    if has_groq:
        row.append(InlineKeyboardButton(text="سريع",        callback_data="ai:platform:groq", style="success"))
    if has_or:
        row.append(InlineKeyboardButton(text="خبير", callback_data="ai:platform:openrouter", style="success"))
    if row:
        rows.append(row)
    try:
        for b in await get_custom_buttons(BOT_USERNAME):
            if not b.get("is_visible", 1):
                continue
            rows.append([_render_custom_button(b)])
    except Exception:
        pass
    return InlineKeyboardMarkup(inline_keyboard=rows) if rows else None


async def _build_model_kb(platform: str) -> InlineKeyboardMarkup:
    enabled = await get_enabled_models(BOT_USERNAME)
    models  = [m for m in enabled if m["platform"] == platform]
    rows, row = [], []
    for m in models:
        name = m["model_name"][:20]
        row.append(InlineKeyboardButton(
            text=name, callback_data=f"ai:model:{m['model_id']}", style="success"))
        if len(row) == 2:
            rows.append(row); row = []
    if row:
        rows.append(row)
    rows.append([InlineKeyboardButton(text="رجوع", callback_data="ai:back", style="primary")])
    return InlineKeyboardMarkup(inline_keyboard=rows)


async def _thinking_animation(message: Message) -> asyncio.Task:
    base = "<b>» جاري التفكير للحصول على اجابة افضل 🚀</b>"
    msg  = await message.answer(base, parse_mode="HTML", disable_web_page_preview=True)

    async def _animate():
        try:
            while True:
                for n in range(0, 5):
                    dots = " •" * n
                    try:
                        await message.bot.edit_message_text(
                            chat_id=msg.chat.id,
                            message_id=msg.message_id,
                            text=(base + " " + dots) if dots else base,
                            parse_mode="HTML", disable_web_page_preview=True,
                        )
                    except TelegramBadRequest:
                        pass
                    except Exception as e:
                        logger.debug(f"animation edit error: {e}")
                    await asyncio.sleep(0.5)
        except asyncio.CancelledError:
            try:
                await message.bot.delete_message(
                    chat_id=msg.chat.id, message_id=msg.message_id
                )
            except Exception:
                pass
            raise

    return asyncio.create_task(_animate())

async def _call_groq(messages: list, model: str, max_retries: int = 3) -> str:
    headers = {
        "Authorization": f"Bearer {GROQ_API_KEY}",
        "Content-Type":  "application/json",
    }
    payload = {"model": model, "messages": messages, "temperature": 0.7}

    for attempt in range(1, max_retries + 1):
        try:
            async with aiohttp.ClientSession() as sess:
                async with sess.post(
                    GROQ_API_URL, headers=headers, json=payload,
                    timeout=aiohttp.ClientTimeout(total=60),
                ) as r:
                    if r.status == 200:
                        data = await r.json()
                        return data["choices"][0]["message"]["content"]
                    raw = await r.text()
                    if r.status == 429:
                        wait = int(r.headers.get("Retry-After", 5))
                        if attempt < max_retries:
                            await asyncio.sleep(min(wait, 15)); continue
                        logger.warning(f"Groq 429 (model={model}): {raw}")
                        return "⚠️ الخادم مزدحم. يرجى المحاولة بعد قليل."
                    if r.status == 400:
                        logger.error(f"Groq 400 (model={model}): {raw}")
                        return "❌ النموذج غير متاح أو تم إيقافه. اختر نموذجاً آخر عبر /start"
                    if 500 <= r.status < 600:
                        logger.warning(f"Groq {r.status} (model={model}): {raw}")
                        if attempt < max_retries:
                            await asyncio.sleep(2 ** attempt); continue
                        return "⚠️ الخادم يواجه مشكلة مؤقتة. حاول لاحقاً."
                    logger.error(f"Groq error {r.status} (model={model}): {raw}")
                    return "❌ حدث خطأ أثناء الاتصال بالخادم. حاول لاحقاً."
        except asyncio.TimeoutError:
            logger.warning(f"Groq timeout attempt {attempt} (model={model})")
            if attempt < max_retries:
                await asyncio.sleep(2); continue
            return "⏱️ انتهت مهلة الاتصال. حاول مرة أخرى."
        except Exception as e:
            logger.error(f"Groq exception attempt {attempt} (model={model}): {e}")
            if attempt < max_retries:
                await asyncio.sleep(2); continue
            return "❌ تعذّر الاتصال بالخادم. تحقق من الاتصال وحاول لاحقاً."
    return "❌ فشل الحصول على رد بعد عدة محاولات."


async def _call_openrouter(messages: list, model: str, max_retries: int = 3) -> str:
    headers = {
        "Authorization": f"Bearer {OPENROUTER_API_KEY}",
        "Content-Type":  "application/json",
        "HTTP-Referer":  "https://kodo.platform",
        "X-Title":       "Kodo Platform Bot",
    }
    payload = {"model": model, "messages": messages, "temperature": 0.7}

    for attempt in range(1, max_retries + 1):
        try:
            async with aiohttp.ClientSession() as sess:
                async with sess.post(
                    OPENROUTER_API_URL, headers=headers, json=payload,
                    timeout=aiohttp.ClientTimeout(total=90),
                ) as r:
                    raw = await r.text()
                    if r.status == 200:
                        import json as _json
                        data = _json.loads(raw)
                        if "choices" in data and data["choices"]:
                            return data["choices"][0]["message"]["content"]
                        logger.warning(f"OpenRouter empty response (model={model}): {raw[:300]}")
                        return "⚠️ النموذج لم يُرجع أي رد. جرّب مرة أخرى."
                    if r.status == 429:
                        try:
                            import json as _json
                            meta     = _json.loads(raw).get("error", {}).get("metadata", {})
                            wait     = int(meta.get("retry_after_seconds", 30))
                            provider = meta.get("provider_name", "المزوّد")
                        except Exception:
                            wait = int(r.headers.get("Retry-After", 30)); provider = "المزوّد"
                        logger.warning(f"OpenRouter 429 (model={model}, provider={provider}, wait={wait}s)")
                        if attempt < max_retries and wait <= 15:
                            await asyncio.sleep(wait); continue
                        return f"⏳ النموذج مزدحم حالياً. انتظر {wait} ثانية ثم أعد المحاولة، أو اختر نموذجاً آخر."
                    if r.status == 404:
                        logger.error(f"OpenRouter 404 (model={model}): {raw[:300]}")
                        return "❌ النموذج غير متاح. اختر نموذجاً آخر عبر /start"
                    if r.status == 401:
                        logger.error(f"OpenRouter 401 (model={model}): {raw[:300]}")
                        return "❌ خطأ في المصادقة. تواصل مع المطور."
                    if r.status == 402:
                        logger.error(f"OpenRouter 402 (model={model}): {raw[:300]}")
                        return "❌ الرصيد غير كافٍ. تواصل مع المطور."
                    if r.status == 400:
                        logger.error(f"OpenRouter 400 (model={model}): {raw[:300]}")
                        return "❌ النموذج لا يقبل هذا الطلب. جرّب نموذجاً آخر."
                    if 500 <= r.status < 600:
                        logger.warning(f"OpenRouter {r.status} (model={model}): {raw[:200]}")
                        if attempt < max_retries:
                            await asyncio.sleep(2 ** attempt); continue
                        return "⚠️ الخادم يواجه مشكلة مؤقتة. حاول لاحقاً."
                    logger.error(f"OpenRouter {r.status} (model={model}): {raw[:300]}")
                    return "❌ حدث خطأ أثناء الاتصال بالخادم. حاول لاحقاً."
        except asyncio.TimeoutError:
            logger.warning(f"OpenRouter timeout attempt {attempt} (model={model})")
            if attempt < max_retries:
                await asyncio.sleep(2); continue
            return "⏱️ انتهت مهلة الاتصال. حاول مرة أخرى."
        except Exception as e:
            logger.error(f"OpenRouter exception attempt {attempt} (model={model}): {e}")
            if attempt < max_retries:
                await asyncio.sleep(2); continue
            return "❌ تعذّر الاتصال بالخادم. تحقق من الاتصال وحاول لاحقاً."
    return "❌ فشل الحصول على رد بعد عدة محاولات."


async def _check_subscription(bot: Bot, user_id: int, event) -> bool:
    is_cb    = isinstance(event, CallbackQuery)
    _answer  = event.message.answer if is_cb else event.answer

    channels = await get_mandatory_channels(BOT_USERNAME)
    for ch in channels:
        try:
            member = await asyncio.wait_for(bot.get_chat_member(int(ch["channel_id"]), user_id), timeout=5)
            if member.status in ("left", "kicked", "banned"):
                link = (ch.get("invite_link") or
                        (f"https://t.me/{ch['channel_username']}" if ch.get("channel_username") else None))
                if ch["display_type"] == "buttons":
                    await _answer(
                        "عزيزي المستخدم اشترك في القناة التالية قبل استخدام البوت:",
                        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
                            [InlineKeyboardButton(
                                text=f"@{ch.get('channel_username') or ch['channel_title']}",
                                url=link or "#"
                            )],
                            [InlineKeyboardButton(text="✅ تحقق من الاشتراك",
                                                  callback_data="ai:check_sub")],
                        ])
                    )
                else:
                    ref = f"<a href='{link}'>{ch['channel_title']}</a>" if link else ch["channel_title"]
                    await _answer(
                        f"عزيزي المستخدم اشترك في القناة:\n- {ref}\n\nثم أرسل /start",
                        parse_mode="HTML", disable_web_page_preview=True)
                return False
        except Exception:
            pass

    funded = await get_funded_channels(BOT_USERNAME)
    for ch in funded:
        try:
            member = await asyncio.wait_for(bot.get_chat_member(int(ch["channel_id"]), user_id), timeout=5)
            if member.status not in ("left", "kicked", "banned"):
                await _handle_funded(bot, user_id, ch)
        except Exception:
            pass
    return True


async def _handle_funded(bot: Bot, user_id: int, ch: dict):
    added = await record_funded_sub(BOT_USERNAME, ch["id"], user_id)
    if not added:
        return
    current, target = await get_funded_channel_count(BOT_USERNAME, ch["id"])
    link = (ch.get("invite_link") or
            (f"https://t.me/{ch['channel_username']}" if ch.get("channel_username")
             else str(ch["channel_id"])))
    if await get_setting(BOT_USERNAME, "fund_notify", "1") == "1":
        await _notify_owner(
            bot,
            f"🔔 <b>اشتراك جديد في قناة ممولة</b>\n"
            f"المستخدم: <code>{user_id}</code>\n"
            f"القناة: <a href='{link}'>{ch['channel_title']}</a>\n"
            f"العداد: <b>{current}/{target}</b>"
        )
    if current >= target:
        await mark_funded_complete(BOT_USERNAME, ch["id"])
        await _notify_owner(
            bot,
            f"✅ <b>اكتملت قناة ممولة!</b>\n"
            f"القناة: <a href='{link}'>{ch['channel_title']}</a> — <b>{target}</b> عضو."
        )

@router.message(CommandStart())
async def cmd_start(message: Message, state: FSMContext, bot: Bot):
    await state.clear()
    u      = message.from_user
    db_usr = await get_or_create_user(BOT_USERNAME, u.id, u.username, u.first_name)

    if db_usr.get("is_banned"):
        await message.answer("⛔ أنت محظور من استخدام هذا البوت.")
        return

    is_open = await get_setting(BOT_USERNAME, "is_open", "1")
    if is_open != "1" and not await _is_owner_or_admin(u.id):
        await message.answer(await get_setting(BOT_USERNAME, "closed_message", "🔴 البوت مقفول حالياً."))
        return

    if not await _check_subscription(bot, u.id, message):
        return

    if db_usr.get("is_new") and await get_setting(BOT_USERNAME, "notify_new_users", "1") == "1":
        name = f"@{u.username}" if u.username else u.first_name
        await _notify_owner(
            bot,
            f"👤 <b>مستخدم جديد!</b>\n"
            f"الاسم: {u.first_name}\nالمعرف: {name}\nID: <code>{u.id}</code>"
        )

    _reset_session(u.id)
    text, w_ents = await _build_welcome(u.first_name, u.id)
    kb   = await _build_platform_kb()

    if await _is_owner_or_admin(u.id):
        panel_text = await _build_dev_panel_text("بوت الذكاء الاصطناعي")
        if await _factory_maker_rights():
            panel_text += f"\n{MAKER_MSG}"
        await message.answer(panel_text, parse_mode="HTML", disable_web_page_preview=True, reply_markup=await _build_dev_panel_kb())

    if w_ents:
        await message.answer(text, entities=w_ents, disable_web_page_preview=True, reply_markup=kb)
    else:
        await message.answer(text, parse_mode="HTML", disable_web_page_preview=True, reply_markup=kb)

@router.message(Command("reset"))
async def cmd_reset(message: Message, state: FSMContext):
    await state.clear()
    u = message.from_user
    usr = await get_user(BOT_USERNAME, u.id)
    if usr and usr.get("is_banned"):
        return
    _reset_session(u.id)
    await message.answer(
        "<b>» تم مسح المحادثة. اختر النموذج للبدء من جديد:</b>",
        reply_markup=_platforms_kb(),
        parse_mode="HTML", disable_web_page_preview=True)
    
    
@router.message(Command("help"))
async def cmd_help(message: Message):
    await message.answer(
        "📚 <b>الأوامر المتاحة:</b>\n\n"
        "/start — بدء محادثة جديدة واختيار النموذج\n"
        "/reset — مسح ذاكرة المحادثة\n"
        "/help  — عرض هذه القائمة\n\n"
        "💬 بعد اختيار النموذج أرسل أي رسالة للبدء.",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=_platforms_kb()
    )

@router.callback_query(F.data == "ai:check_sub")
async def cb_check_sub(call: CallbackQuery, state: FSMContext, bot: Bot):
    if not await _check_subscription(bot, call.from_user.id, call):
        return
    await state.clear()
    text, w_ents = await _build_welcome(call.from_user.first_name, call.from_user.id)
    kb   = await _build_platform_kb()
    if w_ents:
        await call.message.edit_text(text, entities=w_ents, disable_web_page_preview=True, reply_markup=kb)
    else:
        await call.message.edit_text(text, parse_mode="HTML", disable_web_page_preview=True, reply_markup=kb)
    await call.answer()


@router.callback_query(F.data == "ai:back")
async def cb_back(call: CallbackQuery, state: FSMContext):
    await state.clear()
    text, w_ents = await _build_welcome(call.from_user.first_name, call.from_user.id)
    kb   = await _build_platform_kb()
    if w_ents:
        await call.message.edit_text(text, entities=w_ents, disable_web_page_preview=True, reply_markup=kb)
    else:
        await call.message.edit_text(text, parse_mode="HTML", disable_web_page_preview=True, reply_markup=kb)
    await call.answer()


@router.callback_query(F.data.startswith("ai:platform:"))
async def cb_platform(call: CallbackQuery):
    platform = call.data.split(":")[2]
    kb       = await _build_model_kb(platform)
    if len(kb.inline_keyboard) <= 1:
        await call.answer("⚠️ لا توجد نماذج متاحة لهذه المنصة.", show_alert=True)
        return
    label = "السريع" if platform == "groq" else "الخبير"
    await call.message.edit_text(
        f"🧠 <b><i>من فضلك قم بأختيار النموذج المطلوب من نماذج الذكاء الـ</i></b> <code>{label}</code>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=kb
    )
    await call.answer()


@router.callback_query(F.data.startswith("ai:model:"))
async def cb_model(call: CallbackQuery):
    model_id = call.data[len("ai:model:"):]
    models   = await get_enabled_models(BOT_USERNAME)
    model_obj = next((m for m in models if m["model_id"] == model_id), None)
    if model_obj:
        platform = model_obj["platform"]
        name     = model_obj["model_name"]
    else:
        platform = "groq" if "/" not in model_id else "openrouter"
        name     = model_id
    sess     = _get_session(call.from_user.id)
    sess.update({"platform": platform, "model": model_id, "history": []})

    await call.message.edit_text(
        f"» تم اختيار النموذج <b>{name}</b> 🧠.\n» ارسل /reset لمسح الذاكرة وتاريخ المحادثة.\n» يمكنك الان التواصل معي للحصول على اجابات.",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=_platforms_kb()
    )
    await call.answer("✔️ النموذج جاهز")

@router.message(F.text & ~F.text.startswith("/"))
async def handle_chat(message: Message, bot: Bot):
    if message.chat.type != "private":
        return
    u      = message.from_user
    db_usr = await get_or_create_user(BOT_USERNAME, u.id, u.username, u.first_name)

    if db_usr.get("is_banned"):
        await message.answer("⛔ أنت محظور من استخدام هذا البوت.")
        return

    is_open = await get_setting(BOT_USERNAME, "is_open", "1")
    if is_open != "1" and not await _is_owner_or_admin(u.id):
        await message.answer(await get_setting(BOT_USERNAME, "closed_message", "🔴 البوت مقفول."))
        return

    if not await _check_subscription(bot, u.id, message):
        return

    sess = _get_session(u.id)
    if not sess.get("platform") or not sess.get("model"):
        await message.answer(
            "⚠️ لم تختر نموذجاً بعد. اختر المنصة للبدء:",
            reply_markup=_platforms_kb()
        )
        return

    sess["history"].append({"role": "user", "content": message.text})

    system_prompt = await get_setting(BOT_USERNAME, "system_prompt", DEFAULT_SYSTEM_PROMPT)
    if not system_prompt:
        system_prompt = DEFAULT_SYSTEM_PROMPT
    full_messages = [{"role": "system", "content": system_prompt}] + sess["history"]

    tok      = BOT_TOKEN
    chat_id  = message.chat.id
    platform = sess["platform"]
    model    = sess["model"]

    async def _run_chat():
        b = Bot(token=tok)
        anim_task = None
        try:
            base = "<b>» جاري التفكير للحصول على اجابة افضل 🚀</b>"
            anim_msg = await b.send_message(chat_id, base, parse_mode="HTML", disable_web_page_preview=True)

            async def _animate():
                try:
                    while True:
                        for n in range(0, 5):
                            dots = " •" * n
                            try:
                                await b.edit_message_text(
                                    chat_id=chat_id,
                                    message_id=anim_msg.message_id,
                                    text=(base + " " + dots) if dots else base,
                                    parse_mode="HTML", disable_web_page_preview=True,
                                )
                            except TelegramBadRequest:
                                pass
                            except Exception as e:
                                logger.debug(f"animation edit error: {e}")
                            await asyncio.sleep(0.5)
                except asyncio.CancelledError:
                    try:
                        await b.delete_message(chat_id=chat_id, message_id=anim_msg.message_id)
                    except Exception:
                        pass
                    raise

            anim_task = asyncio.create_task(_animate())

            try:
                if platform == "groq":
                    reply = await _call_groq(full_messages, model=model)
                else:
                    reply = await _call_openrouter(full_messages, model=model)
            except Exception as e:
                logger.exception("خطأ غير متوقع في handle_chat")
                reply = "❌ حدث خطأ غير متوقع. حاول لاحقاً."

            anim_task.cancel()
            try:
                await anim_task
            except asyncio.CancelledError:
                pass

            is_error = reply.startswith(("⚠️", "❌", "⏳", "⏱️"))
            if not is_error:
                sess["history"].append({"role": "assistant", "content": reply})
                if len(sess["history"]) > 20:
                    sess["history"] = sess["history"][-20:]

            chunks = [reply[i:i + 4000] for i in range(0, len(reply), 4000)]
            for idx, chunk in enumerate(chunks):
                kb = _platforms_kb() if (is_error and idx == 0) else None
                try:
                    await b.send_message(
                        chat_id, chunk,
                        parse_mode=ParseMode.MARKDOWN, reply_markup=kb,
                        disable_web_page_preview=True
                    )
                except TelegramBadRequest:
                    await b.send_message(
                        chat_id, chunk,
                        parse_mode=None, reply_markup=kb,
                        disable_web_page_preview=True
                    )
        except Exception:
            try:
                await b.send_message(chat_id, "❌ حدث خطأ غير متوقع. حاول لاحقاً.")
            except Exception:
                pass
        finally:
            await b.session.close()

    asyncio.create_task(_run_chat())

async def _build_dev_panel_kb() -> InlineKeyboardMarkup:
    s      = await get_all_settings(BOT_USERNAME)
    is_op  = "🟢 مفتوح" if s.get("is_open",          "1") == "1" else "🔴 مقفول"
    n_blk  = "✅"        if s.get("notify_blocked",   "1") == "1" else "✖️"
    n_new  = "✅"        if s.get("notify_new_users",  "1") == "1" else "✖️"
    n_fund = "✅"        if s.get("fund_notify",       "1") == "1" else "✖️"
    return InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text=f"حالة البوت {is_op}",       callback_data="dev:toggle_open", style="success")],
        [
            InlineKeyboardButton(text=f"اشعار الحظر {n_blk}",  callback_data="dev:toggle_notify_blk", style="success"),
            InlineKeyboardButton(text=f"اشعار الدخول {n_new}", callback_data="dev:toggle_notify_new", style="success"),
        ],
        [InlineKeyboardButton(text=f"اشعار الممولة {n_fund}",   callback_data="dev:toggle_notify_fund", style="success")],
        [
            InlineKeyboardButton(text="قسم الاذاعة",             callback_data="dev:broadcast", style="primary"),
            InlineKeyboardButton(text="قسم الاحصائيات",          callback_data="dev:stats", style="primary"),
        ],
        [
            InlineKeyboardButton(text="الاشتراك الاجباري",       callback_data="dev:mand", style="primary"),
            InlineKeyboardButton(text="قسم الادمنية",            callback_data="dev:admins", style="primary"),
        ],
        [InlineKeyboardButton(text="القنوات الممولة",            callback_data="dev:funded", style="danger")],
        [InlineKeyboardButton(text="تعديل رسالة الترحيب",        callback_data="dev:welcome", style="danger")],
        [InlineKeyboardButton(text="النماذج الحالية",          callback_data="dev:models", style="danger")],
        [
            InlineKeyboardButton(text="اضافة زر",     callback_data="cbtn:add", style="success"),
            InlineKeyboardButton(text="ادارة الازرار", callback_data="cbtn:manage", style="primary"),
        ],
    ])


@dev_router.callback_query(F.data == "dev:panel")
async def cb_dev_panel(call: CallbackQuery, state: FSMContext):
    await state.clear()
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await call.message.edit_text(
        "» <b>هذه هي لوحة التحكم والإدارة بوت الذكاء الاصطناعي الخاص بك</b>.\n\n» <b>لوحة تحكم متكاملة تتيح لك الإشراف على صلاحيات البوت، متابعة الإحصائيات الفورية وإدارة القنوات وكل شيء من مكان واحد</b>.\n\n» <b>قم بأختيار الاجراء المطلوب من القائمة ادناه</b>:", parse_mode="HTML", disable_web_page_preview=True, reply_markup=await _build_dev_panel_kb()
    )
    await call.answer()


@dev_router.callback_query(F.data == "dev:toggle_open")
async def cb_toggle_open(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    cur = await get_setting(BOT_USERNAME, "is_open", "1")
    await set_setting(BOT_USERNAME, "is_open", "0" if cur == "1" else "1")
    await call.message.edit_reply_markup(reply_markup=await _build_dev_panel_kb())
    await call.answer()


@dev_router.callback_query(F.data == "dev:toggle_notify_blk")
async def cb_toggle_notify_blk(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    cur = await get_setting(BOT_USERNAME, "notify_blocked", "1")
    await set_setting(BOT_USERNAME, "notify_blocked", "0" if cur == "1" else "1")
    await call.message.edit_reply_markup(reply_markup=await _build_dev_panel_kb())
    await call.answer()


@dev_router.callback_query(F.data == "dev:toggle_notify_new")
async def cb_toggle_notify_new(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    cur = await get_setting(BOT_USERNAME, "notify_new_users", "1")
    await set_setting(BOT_USERNAME, "notify_new_users", "0" if cur == "1" else "1")
    await call.message.edit_reply_markup(reply_markup=await _build_dev_panel_kb())
    await call.answer()


@dev_router.callback_query(F.data == "dev:toggle_notify_fund")
async def cb_toggle_notify_fund(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    cur = await get_setting(BOT_USERNAME, "fund_notify", "1")
    await set_setting(BOT_USERNAME, "fund_notify", "0" if cur == "1" else "1")
    await call.message.edit_reply_markup(reply_markup=await _build_dev_panel_kb())
    await call.answer()


async def _build_models_kb() -> InlineKeyboardMarkup:
    all_models = await get_models(BOT_USERNAME)
    rows, row  = [], []
    for m in all_models:
        icon = "✅" if m["is_enabled"] else "✖️"
        name = m["model_name"]
        if len(name) > 12:
            name = name[:10] + ".."
        row.append(InlineKeyboardButton(
            text=f"{icon} {name}",
            callback_data=f"devmodel:{m['model_id']}", style="success"))
        if len(row) == 2:
            rows.append(row); row = []
    if row:
        rows.append(row)
    rows.append([InlineKeyboardButton(text="رجوع", callback_data="dev:panel", style="primary")])
    return InlineKeyboardMarkup(inline_keyboard=rows)


@dev_router.callback_query(F.data == "dev:models")
async def cb_dev_models(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await call.message.edit_text(
        "» <b>عزيزي المطور هذه هي قائمة بجميع النماذج الحالية</b>.\n» <b>معروض في لوحة المستخدمين</b> = ✅\n» <b>مخفي من لوحة المستخدمين</b> = ✖️",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=await _build_models_kb()
    )
    await call.answer()


@dev_router.callback_query(F.data.startswith("devmodel:"))
async def cb_toggle_model(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    model_id = call.data[len("devmodel:"):]
    await toggle_model(BOT_USERNAME, model_id)
    await call.message.edit_reply_markup(reply_markup=await _build_models_kb())
    await call.answer()


@dev_router.callback_query(F.data == "dev:stats")
async def cb_stats(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    s = await get_statistics(BOT_USERNAME)
    await call.message.edit_text(
        f"» <b>عزيزي المطور هذا قسم احصائيات البوت</b>:\n\n» <b>عدد المستخدمين: {s['total']} مستخدم</b>\n» <b>النشطون حالياً: {s['active']} مستخدم</b>\n\n» <b>المحضورين: {s['banned']} مستخدم</b>\n» <b>حظرو البوت: {s['blocked']} مستخدم</b>",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:panel", style="primary")]
        ])
    )
    await call.answer()


@dev_router.callback_query(F.data == "dev:welcome")
async def cb_welcome(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    cur = await get_setting(BOT_USERNAME, "welcome_message", "")
    await state.set_state(DevStates.waiting_welcome)
    await call.message.edit_text(
        f"✏️ أرسل رسالة الترحيب الجديدة.\n"
        f"يمكنك استخدام <code>{{first_name}}</code> لاسم المستخدم.\n\n"
        f"الحالية:\n{cur or '<i>افتراضية</i>'}",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="◀️ رجوع", callback_data="dev:panel", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_welcome)
async def process_welcome(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    await set_setting(BOT_USERNAME, "welcome_message", message.text or message.html_text or "")
    await set_setting(BOT_USERNAME, "welcome_message_entities", _entities_to_json(message.entities) or "")
    await state.clear()
    await message.answer(
        "✅ تم تحديث رسالة الترحيب.",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text=" لوحة التحكم", callback_data="dev:panel", style="primary")]
        ])
    )


@dev_router.callback_query(F.data == "dev:broadcast")
async def cb_broadcast(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await state.set_state(DevStates.waiting_broadcast)
    await call.message.edit_text(
        "» <b>عزيزي المطور هذا هو قسم الاذاعة بالبوت</b>.\n\n» <b>ارسل رسالتك الان لعمل اذاعة للمستخدمين أو قم بالضغط على زر اذاعة بالتوجيه ثم ارسل رسالة موجهة لارسالها</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="إذاعة بالتوجيه", callback_data="dev:bcast_fwd", style="primary")],
            [InlineKeyboardButton(text="رجوع",         callback_data="dev:panel", style="primary")],
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_broadcast)
async def process_broadcast(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    await state.clear()
    wait = await message.answer("⏳ جاري الإذاعة...")
    asyncio.create_task(_do_broadcast(message, wait, forward=False))


@dev_router.callback_query(F.data == "dev:bcast_fwd")
async def cb_bcast_fwd(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await state.set_state(DevStates.waiting_broadcast_fwd)
    await call.message.edit_text(
        "» <b>قم بتوجية الرسالة التي تريد اذاعتها الان</b> 📨 :",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:broadcast", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_broadcast_fwd)
async def process_broadcast_fwd(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    await state.clear()
    wait = await message.answer("⏳ جاري الإذاعة...")
    asyncio.create_task(_do_broadcast(message, wait, forward=True))


async def _do_broadcast(message: Message, wait: Message | None = None, forward: bool = False):
    uname   = BOT_USERNAME
    tok     = BOT_TOKEN
    chat_id = message.chat.id
    users = await get_all_active_users(uname)
    sent = failed = 0
    b = Bot(token=tok)
    try:
        for i, u in enumerate(users):
            try:
                if forward:
                    await b.forward_message(u["id"], from_chat_id=chat_id, message_id=message.message_id)
                else:
                    await b.copy_message(u["id"], from_chat_id=chat_id, message_id=message.message_id)
                sent += 1
            except Exception:
                failed += 1
            if (i + 1) % BROADCAST_BATCH == 0:
                await asyncio.sleep(BROADCAST_DELAY)
        if wait:
            await b.edit_message_text(
                chat_id=chat_id, message_id=wait.message_id,
                text=(f"» <b>تم ارسال الاذاعة الى كافة مستخدمين البوت</b>.\n\n"
                      f"» <b>نجـح الارسال إلى: {sent} مستخدم</b>\n"
                      f"» <b>فشل الارسال إلى: {failed} مستخدم</b>"),
                parse_mode="HTML", disable_web_page_preview=True,
                reply_markup=InlineKeyboardMarkup(inline_keyboard=[
                    [InlineKeyboardButton(text="لوحة التحكم", callback_data="dev:panel", style="primary")]
                ])
            )
    except Exception:
        pass
    finally:
        await b.session.close()


async def _render_admins(call: CallbackQuery):
    admins = await get_admins(BOT_USERNAME)
    text   = "» <b>عزيزي المطور هذا هو قسم التحكم بالادمنية</b>.\n» <b>يمكن اضافة وحذف وعرض الادمنية من الازرار</b>:\n"
    rows   = []
    for a in admins:
        name  = f"@{a['username']}" if a.get("username") else a["first_name"]
        text += f"» {name} (<code>{a['id']}</code>)\n"
        rows.append([InlineKeyboardButton(
            text=f"🗑 {name}", callback_data=f"dev:del_admin:{a['id']}", style="danger"
        )])
    if not admins:
        text += "لا يوجد أدمنية."
    rows += [
        [InlineKeyboardButton(text="إضافة أدمن",  callback_data="dev:add_admin", style="success")],
        [InlineKeyboardButton(text="حظر مستخدم",  callback_data="dev:ban", style="danger"), InlineKeyboardButton(text="رفع الحظر",   callback_data="dev:unban", style="danger")],
        [InlineKeyboardButton(text="رجوع",         callback_data="dev:panel", style="primary")],
    ]
    await call.message.edit_text(
        text, parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=rows)
    )


@dev_router.callback_query(F.data == "dev:admins")
async def cb_admins(call: CallbackQuery, state: FSMContext):
    await state.clear()
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await _render_admins(call); await call.answer()


@dev_router.callback_query(F.data == "dev:add_admin")
async def cb_add_admin(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await state.set_state(DevStates.waiting_add_admin)
    await call.message.edit_text(
        "» <b>أرسل أيدي المستخدم الذي تريد اضافته ادمن في البوت</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:admins", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_add_admin)
async def process_add_admin(message: Message, state: FSMContext, bot: Bot):
    if not await _is_owner_or_admin(message.from_user.id): return
    try:
        admin_id = int(message.text.strip())
    except (ValueError, AttributeError):
        await message.answer("⚠️ أرسل ID رقمي صحيح!"); return
    try:
        info = await bot.get_chat(admin_id)
        fn, un = info.first_name or str(admin_id), info.username
    except Exception:
        fn, un = str(admin_id), None
    await add_admin(BOT_USERNAME, admin_id, un, fn)
    await state.clear()
    await message.answer(
        f"» <b>تم اضافة المستخدم <code>{admin_id}</code> كأدمن في البوت</b>.", parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="قسم الادمنية", callback_data="dev:admins", style="primary")]
        ])
    )


@dev_router.callback_query(F.data.startswith("dev:del_admin:"))
async def cb_del_admin(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await remove_admin(BOT_USERNAME, int(call.data.split(":")[2]))
    await call.answer("✅ تم حذف الأدمن.", show_alert=True)
    await _render_admins(call)


@dev_router.callback_query(F.data == "dev:ban")
async def cb_ban(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await state.set_state(DevStates.waiting_ban_id)
    await call.message.edit_text(
        "» <b>أرسل أيدي المستخدم الذي تريد حظره من استخدام البوت</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:admins", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_ban_id)
async def process_ban(message: Message, state: FSMContext, bot: Bot):
    if not await _is_owner_or_admin(message.from_user.id): return
    try:
        ban_id = int(message.text.strip())
    except (ValueError, AttributeError):
        await message.answer("⚠️ أرسل ID رقمي صحيح!"); return
    result = await ban_user(BOT_USERNAME, ban_id)
    await state.clear()
    if not result:
        await message.answer("⚠️ المستخدم غير موجود في قاعدة البيانات."); return
    if await get_setting(BOT_USERNAME, "notify_blocked", "1") == "1":
        await _notify_owner(bot, f"🚫 تم حظر المستخدم <code>{ban_id}</code>.")
    await message.answer(
        f"✅ تم حظر <code>{ban_id}</code>.", parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:admins", style="primary")]
        ])
    )


@dev_router.callback_query(F.data == "dev:unban")
async def cb_unban(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await state.set_state(DevStates.waiting_unban_id)
    await call.message.edit_text(
        "» <b>أرسل أيدي المستخدم الذي تريد رفع الحظر عنه في البوت</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:admins", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_unban_id)
async def process_unban(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    try:
        uid = int(message.text.strip())
    except (ValueError, AttributeError):
        await message.answer("⚠️ أرسل ID رقمي صحيح!"); return
    await unban_user(BOT_USERNAME, uid)
    await state.clear()
    await message.answer(
        f"✅ تم رفع الحظر عن <code>{uid}</code>.", parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:admins", style="primary")]
        ])
    )

async def _render_mand(call: CallbackQuery):
    channels = await get_mandatory_channels(BOT_USERNAME)
    text     = "» <b>عزيزي المطور هذا هو قسم الاشتراك الاجباري</b>.\n» <b>يمكن اضافة وحذف وعرض القنوات من الازرار</b>:\n\n"
    rows     = []
    for ch in channels:
        name  = f"@{ch['channel_username']}" if ch.get("channel_username") else ch["channel_title"]
        
        link = ch.get("invite_link")
        if not link and ch.get("channel_username"):
            link = f"https://t.me/{ch['channel_username']}"
        if not link:
            cid = str(ch["channel_id"]).replace("-100", "")
            link = f"https://t.me/c/{cid}"
        
        text += f"• <a href='{link}'>{name}</a>\n"
        
        rows.append([
            InlineKeyboardButton(text=f"{name}", url=link, style="success"),
            InlineKeyboardButton(text=f"🗑", callback_data=f"dev:del_mand:{ch['id']}", style="danger")
        ])
    if not channels:
        text += "لا توجد قنوات."
    rows += [
        [InlineKeyboardButton(text="إضافة قناة", callback_data="dev:add_mand", style="success")],
        [InlineKeyboardButton(text="رجوع",        callback_data="dev:panel", style="primary")],
    ]
    await call.message.edit_text(
        text, parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=rows)
    )


@dev_router.callback_query(F.data == "dev:mand")
async def cb_mand(call: CallbackQuery, state: FSMContext):
    await state.clear()
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await _render_mand(call); await call.answer()


@dev_router.callback_query(F.data == "dev:add_mand")
async def cb_add_mand(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await call.message.edit_text(
        "» <b>اختر الطريقة التي تريد عرض طلب الاشتراك فيها من المستخدم</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [
                InlineKeyboardButton(text="ازرار",  callback_data="mand_type:buttons", style="success"),
                InlineKeyboardButton(text="رسالة نصية",  callback_data="mand_type:text", style="success"),
            ],
            [InlineKeyboardButton(text="رجوع", callback_data="dev:mand", style="primary")],
        ])
    )
    await call.answer()


@dev_router.callback_query(F.data.startswith("mand_type:"))
async def cb_mand_type(call: CallbackQuery, state: FSMContext):
    await state.update_data(mand_display_type=call.data.split(":")[1])
    await state.set_state(DevStates.waiting_mand_fwd)
    await call.message.edit_text(
        "» <b>ممتاز، الان قم بتوجيه رسالة من القناة/المجموعة لاضافتها</b>.\n⚠️ <b>يجب ان يكون البوت مشرفاً في القناة/المجموعة</b>.",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:mand", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_mand_fwd)
async def process_mand_fwd(message: Message, state: FSMContext, bot: Bot):
    if not await _is_owner_or_admin(message.from_user.id): return
    if not message.forward_from_chat:
        await message.answer("⚠️ يرجى توجيه رسالة من القناة فقط!"); return
    chat = message.forward_from_chat
    try:
        me = await bot.get_me()
        mb = await bot.get_chat_member(chat.id, me.id)
        if mb.status not in ("administrator", "creator"):
            await message.answer("⚠️ يجب إضافة البوت كمشرف في القناة أولاً!")
            await state.clear(); return
    except Exception:
        await message.answer("⚠️ لا يمكن الوصول للقناة."); await state.clear(); return
    link = f"https://t.me/{chat.username}" if chat.username else None
    if not link:
        try:
            link = (await bot.create_chat_invite_link(chat.id)).invite_link
        except Exception: pass
    d = await state.get_data()
    await add_mandatory_channel(
        BOT_USERNAME, str(chat.id), chat.title or str(chat.id),
        chat.username, link, d.get("mand_display_type", "buttons")
    )
    await state.clear()
    await message.answer(
        f"✅ تمت إضافة القناة <b>{chat.title}</b>.", parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="الاشتراك الإجباري", callback_data="dev:mand", style="primary")]
        ])
    )


@dev_router.callback_query(F.data.startswith("dev:del_mand:"))
async def cb_del_mand(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await remove_mandatory_channel(BOT_USERNAME, int(call.data.split(":")[2]))
    await call.answer("✅ تم حذف القناة.", show_alert=True)
    await _render_mand(call)


async def _render_funded(call: CallbackQuery):
    channels = await get_funded_channels(BOT_USERNAME)
    text     = "» <b>عزيزي المطور هذا هو قسم تمويل القنوات</b>.\n\n» <b>هذا القسم مختص في اجراء تمويل لقناة انت من يحددها حيث سيطلب البوت من المستخدمين الاشتراك في هذه القناة، عند اكتمال العدد المطلوب الذي تحدده انت سيتم ازالة القناة من القنوات الممولة</b>.\n\n"
    rows     = []
    for ch in channels:
        name  = f"@{ch['channel_username']}" if ch.get("channel_username") else ch["channel_title"]
        
        link = ch.get("invite_link")
        if not link and ch.get("channel_username"):
            link = f"https://t.me/{ch['channel_username']}"
        if not link:
            cid = str(ch["channel_id"]).replace("-100", "")
            link = f"https://t.me/c/{cid}"

        text += f"• <a href='{link}'>{name}</a> ({ch['current_count']}/{ch['target_count']})\n"
        
        rows.append([
            InlineKeyboardButton(text=f"{name}", url=link, style="success"),
            InlineKeyboardButton(text=f"🗑", callback_data=f"dev:del_funded:{ch['id']}", style="danger")
        ])
    if not channels:
        text += "لا توجد قنوات."
    rows += [
        [InlineKeyboardButton(text="اضافة قناة", callback_data="dev:add_funded", style="success")],
        [InlineKeyboardButton(text="رجوع",              callback_data="dev:panel", style="primary")],
    ]
    await call.message.edit_text(
        text, parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=rows)
    )

@dev_router.callback_query(F.data == "dev:funded")
async def cb_funded(call: CallbackQuery, state: FSMContext):
    await state.clear()
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await _render_funded(call); await call.answer()


@dev_router.callback_query(F.data == "dev:add_funded")
async def cb_add_funded(call: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await call.message.edit_text(
        "» <b>اختر الطريقة التي تريد عرض طلب الاشتراك فيها من المستخدم</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [
                InlineKeyboardButton(text="ازرار",  callback_data="funded_type:buttons", style="success"),
                InlineKeyboardButton(text="رسالة نصية",  callback_data="funded_type:text", style="success"),
            ],
            [InlineKeyboardButton(text="رجوع", callback_data="dev:funded", style="primary")],
        ])
    )
    await call.answer()


@dev_router.callback_query(F.data.startswith("funded_type:"))
async def cb_funded_type(call: CallbackQuery, state: FSMContext):
    await state.update_data(funded_display_type=call.data.split(":")[1])
    await state.set_state(DevStates.waiting_funded_fwd)
    await call.message.edit_text(
        "» <b>ممتاز، الان قم بتوجيه رسالة من القناة/المجموعة لاضافتها</b>.\n⚠️ <b>يجب ان يكون البوت مشرفاً في القناة/المجموعة</b>.",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:funded", style="primary")]
        ])
    )
    await call.answer()


@dev_router.message(DevStates.waiting_funded_fwd)
async def process_funded_fwd(message: Message, state: FSMContext, bot: Bot):
    if not await _is_owner_or_admin(message.from_user.id): return
    if not message.forward_from_chat:
        await message.answer("⚠️ يرجى توجيه رسالة من القناة فقط!"); return
    chat = message.forward_from_chat
    try:
        me = await bot.get_me()
        mb = await bot.get_chat_member(chat.id, me.id)
        if mb.status not in ("administrator", "creator"):
            await message.answer("⚠️ يجب إضافة البوت كمشرف في القناة أولاً!")
            await state.clear(); return
    except Exception:
        await message.answer("⚠️ لا يمكن الوصول للقناة."); await state.clear(); return
    link = f"https://t.me/{chat.username}" if chat.username else None
    if not link:
        try:
            link = (await bot.create_chat_invite_link(chat.id)).invite_link
        except Exception: pass
    await state.update_data(
        funded_chat_id=str(chat.id), funded_title=chat.title or str(chat.id),
        funded_username=chat.username, funded_invite=link,
    )
    await state.set_state(DevStates.waiting_funded_count)
    await message.answer(
        f"» <b>القناة</b>: {chat.title}\n» <b>قم بإرسال عدد المشتركين المستهدف الان</b>:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="dev:funded", style="primary")]
        ])
    )


@dev_router.message(DevStates.waiting_funded_count)
async def process_funded_count(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    try:
        target = int(message.text.strip())
        if target <= 0: raise ValueError
    except (ValueError, AttributeError):
        await message.answer("⚠️ أرسل رقماً صحيحاً أكبر من صفر!"); return
    d = await state.get_data()
    await add_funded_channel(
        BOT_USERNAME, d["funded_chat_id"], d["funded_title"],
        d.get("funded_username"), d.get("funded_invite"),
        d.get("funded_display_type", "buttons"), target,
    )
    await state.clear()
    await message.answer(
        f"» <b>تم اضافة القناة: {d['funded_title']} بنجاح</b>.\n» <b>الهدف: {target} عضو</b>",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="القنوات الممولة", callback_data="dev:funded", style="primary")]
        ])
    )


@dev_router.callback_query(F.data.startswith("dev:del_funded:"))
async def cb_del_funded(call: CallbackQuery):
    if not await _is_owner_or_admin(call.from_user.id):
        await call.answer("⛔ غير مصرح!", show_alert=True); return
    await remove_funded_channel(BOT_USERNAME, int(call.data.split(":")[2]))
    await call.answer("✅ تم حذف القناة.", show_alert=True)
    await _render_funded(call)


def _render_custom_button(b: dict):
    label = b.get("label") or ""
    emoji = b.get("emoji")
    if emoji:
        label = f"{emoji} {label}"
    kwargs = {"text": label}
    if b["btn_type"] == "url":
        kwargs["url"] = b["content"]
    else:
        kwargs["callback_data"] = f"cbtn:press:{b['id']}"
    color = b.get("color")
    if color and color != "default":
        style_map = {"green": "success", "blue": "primary", "red": "danger"}
        tg = style_map.get(color)
        if tg:
            kwargs["style"] = tg
    if b.get("custom_emoji_id"):
        kwargs["icon_custom_emoji_id"] = b["custom_emoji_id"]
    return InlineKeyboardButton(**kwargs)


@dev_router.callback_query(F.data == "cbtn:add")
async def cbtn_add(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    await state.clear()
    await callback.message.edit_text(
        "» قم بأختيار نوع الزر الذي سيظهر للمستخدم، يُمكن ان يكون رابط أو كالباك، اختر من الازرار:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="url - رابط",     callback_data="cbtn:type:url", style="success")],
            [InlineKeyboardButton(text="callback - كالباك", callback_data="cbtn:type:callback", style="success")],
            [InlineKeyboardButton(text="رجوع", callback_data="dev:panel", style="primary")],
        ])
    )
    await callback.answer()


@dev_router.callback_query(F.data.startswith("cbtn:type:"))
async def cbtn_type(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_type = callback.data.split(":")[2]
    await state.update_data(cbtn_type=btn_type)
    await state.set_state(DevStates.waiting_cbtn_label)
    await callback.message.edit_text(
        "» أرسل اسم الزر الذي سيظهر للمستخدم:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="cbtn:add", style="primary")]
        ])
    )
    await callback.answer()


@dev_router.message(DevStates.waiting_cbtn_label)
async def cbtn_process_label(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    if message.text and message.text.startswith("/"):
        await state.clear(); return
    label = (message.text or "").strip()
    if not label:
        await message.answer("⚠️ أرسل اسماً صحيحاً للزر!"); return
    data = await state.get_data()
    btn_type = data.get("cbtn_type")
    await state.update_data(cbtn_label=label)
    await state.set_state(DevStates.waiting_cbtn_content)
    prompt = "» أرسل الرابط الآن:" if btn_type == "url" else "» أرسل الرسالة النصية التي ستظهر عند الضغط على الزر:"
    await message.answer(
        prompt, parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="cbtn:add", style="primary")]
        ])
    )


@dev_router.message(DevStates.waiting_cbtn_content)
async def cbtn_process_content(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    if message.text and message.text.startswith("/"):
        await state.clear(); return
    content = (message.text or "").strip()
    data = await state.get_data()
    btn_type = data.get("cbtn_type")
    label    = data.get("cbtn_label")
    if btn_type == "url":
        if not (content.startswith("http://") or content.startswith("https://") or content.startswith("tg://")):
            await message.answer("⚠️ أرسل رابطاً صحيحاً يبدأ بـ https:// أو http://")
            return
    if not content:
        await message.answer("⚠️ المحتوى فارغ!"); return
    await add_custom_button(BOT_USERNAME, btn_type, label, content)
    await state.clear()
    await message.answer(
        "✅ تمت إضافة الزر بنجاح، وسيظهر للمستخدمين الآن.",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text=" لوحة التحكم", callback_data="dev:panel", style="primary")]
        ])
    )


@dev_router.callback_query(F.data == "cbtn:manage")
async def cbtn_manage(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    await state.clear()
    buttons = await get_custom_buttons(BOT_USERNAME)
    if not buttons:
        await callback.answer("لا توجد أزرار مضافة.", show_alert=True); return
    rows = [[InlineKeyboardButton(text=b["label"], callback_data=f"cbtn:view:{b['id']}", style="success")] for b in buttons]
    rows.append([InlineKeyboardButton(text="رجوع", callback_data="dev:panel", style="primary")])
    await callback.message.edit_text(
        "عزيزي المطور اختر الزر المراد ادارته:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=rows)
    )
    await callback.answer()


async def _cbtn_view_render(callback: CallbackQuery, btn_id: int):
    b = await get_custom_button(BOT_USERNAME, btn_id)
    if not b:
        await callback.answer("الزر غير موجود.", show_alert=True); return
    vis = "ظاهر ✅" if b.get("is_visible", 1) else "مخفي 🚫"
    await callback.message.edit_text(
        f"اسم الزر: <b>{b['label']}</b>\nالحالة: {vis}\n\n"
        "عزيزي المطور يمكنك ادارة الزر المضاف:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="🗑 حذف الزر",        callback_data=f"cbtn:del:{btn_id}", style="danger")],
            [InlineKeyboardButton(text="✏️ تعديل محتوى الزر", callback_data=f"cbtn:edit:{btn_id}", style="success")],
            [InlineKeyboardButton(text="📝 تعديل اسم الزر",   callback_data=f"cbtn:ename:{btn_id}", style="success")],
            [InlineKeyboardButton(text="🎨 تعديل لون الزر",   callback_data=f"cbtn:ecolor:{btn_id}", style="success")],
            [InlineKeyboardButton(text="😀 تعديل ايموجي الزر", callback_data=f"cbtn:eemoji:{btn_id}", style="success")],
            [InlineKeyboardButton(text="👁 اخفاء/اظهار الزر",  callback_data=f"cbtn:toggle:{btn_id}", style="primary")],
            [InlineKeyboardButton(text="رجوع", callback_data="cbtn:manage", style="primary")],
        ])
    )
    await callback.answer()


@dev_router.callback_query(F.data.startswith("cbtn:view:"))
async def cbtn_view(callback: CallbackQuery):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    await _cbtn_view_render(callback, int(callback.data.split(":")[2]))


@dev_router.callback_query(F.data.startswith("cbtn:del:"))
async def cbtn_del(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    await delete_custom_button(BOT_USERNAME, btn_id)
    await callback.answer("✅ تم حذف الزر.", show_alert=True)
    await cbtn_manage(callback, state)


@dev_router.callback_query(F.data.startswith("cbtn:toggle:"))
async def cbtn_toggle(callback: CallbackQuery):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    b = await get_custom_button(BOT_USERNAME, btn_id)
    if not b:
        await callback.answer("الزر غير موجود.", show_alert=True); return
    new_vis = 0 if b.get("is_visible", 1) else 1
    await update_custom_button(BOT_USERNAME, btn_id, is_visible=new_vis)
    await callback.answer("✅ تم.", show_alert=False)
    await _cbtn_view_render(callback, btn_id)


@dev_router.callback_query(F.data.startswith("cbtn:ecolor:"))
async def cbtn_ecolor(callback: CallbackQuery):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    await callback.message.edit_text(
        "🎨 اختر لون الزر:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="🟢 أخضر", callback_data=f"cbtn:setcolor:{btn_id}:green", style="success"),
             InlineKeyboardButton(text="🔵 أزرق", callback_data=f"cbtn:setcolor:{btn_id}:blue", style="primary")],
            [InlineKeyboardButton(text="🔴 أحمر", callback_data=f"cbtn:setcolor:{btn_id}:red", style="danger"),
             InlineKeyboardButton(text="⚪ افتراضي", callback_data=f"cbtn:setcolor:{btn_id}:default")],
            [InlineKeyboardButton(text="رجوع", callback_data=f"cbtn:view:{btn_id}", style="primary")],
        ])
    )
    await callback.answer()


@dev_router.callback_query(F.data.startswith("cbtn:setcolor:"))
async def cbtn_setcolor(callback: CallbackQuery):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    parts  = callback.data.split(":")
    btn_id = int(parts[2]); color = parts[3]
    await update_custom_button(BOT_USERNAME, btn_id, color=color)
    await callback.answer("✅ تم تغيير اللون.", show_alert=False)
    await _cbtn_view_render(callback, btn_id)


@dev_router.callback_query(F.data.startswith("cbtn:eemoji:"))
async def cbtn_eemoji(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    await state.update_data(cbtn_emoji_id=btn_id)
    await state.set_state(DevStates.waiting_cbtn_emoji)
    await callback.message.edit_text(
        "😀 أرسل الإيموجي الجديد (عادي أو متحرك مميز)، أو أرسل <code>-</code> لحذف الإيموجي:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data=f"cbtn:view:{btn_id}", style="primary")]
        ])
    )
    await callback.answer()


@dev_router.message(DevStates.waiting_cbtn_emoji)
async def cbtn_process_emoji(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    if message.text and message.text.startswith("/"):
        await state.clear(); return
    data   = await state.get_data()
    btn_id = data.get("cbtn_emoji_id")
    text   = (message.text or "").strip()
    if text == "-":
        await update_custom_button(BOT_USERNAME, btn_id, emoji=None, custom_emoji_id=None)
        await state.clear()
        await message.answer("✅ تم حذف الإيموجي.", parse_mode="HTML",
            reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="ادارة الازرار", callback_data="cbtn:manage", style="primary")]]))
        return
    custom_emoji_id = None
    if message.entities:
        for e in message.entities:
            if e.type == "custom_emoji":
                custom_emoji_id = e.custom_emoji_id
                break
    if custom_emoji_id:
        await update_custom_button(BOT_USERNAME, btn_id, custom_emoji_id=custom_emoji_id, emoji=None)
        msg = "✅ تم تعيين الإيموجي المتحرك بنجاح!"
    else:
        await update_custom_button(BOT_USERNAME, btn_id, emoji=text, custom_emoji_id=None)
        msg = f"✅ تم تعيين الإيموجي: {text}"
    await state.clear()
    await message.answer(msg, parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="ادارة الازرار", callback_data="cbtn:manage", style="primary")]]))


@dev_router.callback_query(F.data.startswith("cbtn:ename:"))
async def cbtn_ename(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    await state.update_data(cbtn_ename_id=btn_id)
    await state.set_state(DevStates.waiting_cbtn_name)
    await callback.message.edit_text(
        "📝 أرسل الاسم الجديد للزر:",
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data=f"cbtn:view:{btn_id}", style="primary")]
        ])
    )
    await callback.answer()


@dev_router.message(DevStates.waiting_cbtn_name)
async def cbtn_process_name(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    if message.text and message.text.startswith("/"):
        await state.clear(); return
    label = (message.text or "").strip()
    if not label:
        await message.answer("⚠️ أرسل اسماً صحيحاً!"); return
    data   = await state.get_data()
    btn_id = data.get("cbtn_ename_id")
    await update_custom_button(BOT_USERNAME, btn_id, label=label)
    await state.clear()
    await message.answer("✅ تم تعديل اسم الزر.", parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="ادارة الازرار", callback_data="cbtn:manage", style="primary")]]))


@dev_router.callback_query(F.data.startswith("cbtn:edit:"))
async def cbtn_edit(callback: CallbackQuery, state: FSMContext):
    if not await _is_owner_or_admin(callback.from_user.id):
        await callback.answer("⛔ غير مصرح!", show_alert=True); return
    btn_id = int(callback.data.split(":")[2])
    b = await get_custom_button(BOT_USERNAME, btn_id)
    if not b:
        await callback.answer("الزر غير موجود.", show_alert=True); return
    await state.update_data(cbtn_edit_id=btn_id, cbtn_edit_type=b["btn_type"])
    await state.set_state(DevStates.waiting_cbtn_edit_content)
    prompt = "» أرسل الرابط الجديد:" if b["btn_type"] == "url" else "» أرسل الرسالة النصية الجديدة:"
    await callback.message.edit_text(
        prompt, parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data=f"cbtn:view:{btn_id}", style="primary")]
        ])
    )
    await callback.answer()


@dev_router.message(DevStates.waiting_cbtn_edit_content)
async def cbtn_process_edit_content(message: Message, state: FSMContext):
    if not await _is_owner_or_admin(message.from_user.id): return
    if message.text and message.text.startswith("/"):
        await state.clear(); return
    content = (message.text or "").strip()
    data = await state.get_data()
    btn_type = data.get("cbtn_edit_type")
    btn_id   = data.get("cbtn_edit_id")
    if btn_type == "url" and not (content.startswith("http://") or content.startswith("https://") or content.startswith("tg://")):
        await message.answer("⚠️ أرسل رابطاً صحيحاً!"); return
    if not content:
        await message.answer("⚠️ المحتوى فارغ!"); return
    await update_custom_button(BOT_USERNAME, btn_id, content=content)
    await state.clear()
    await message.answer("✅ تم تعديل محتوى الزر بنجاح.", parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="ادارة الازرار", callback_data="cbtn:manage", style="primary")]]))


@router.callback_query(F.data.startswith("cbtn:press:"))
async def cbtn_press(callback: CallbackQuery):
    btn_id = int(callback.data.split(":")[2])
    b = await get_custom_button(BOT_USERNAME, btn_id)
    if not b:
        await callback.answer("هذا الزر لم يعد متاحاً.", show_alert=True); return
    await callback.message.edit_text(
        b["content"],
        parse_mode="HTML", disable_web_page_preview=True,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="رجوع", callback_data="ai:back", style="primary")]
        ])
    )
    await callback.answer()


_ai_dispatcher: Dispatcher | None = None


def get_ai_dispatcher() -> Dispatcher:
    global _ai_dispatcher
    if _ai_dispatcher is None:
        storage        = MemoryStorage()
        _ai_dispatcher = Dispatcher(storage=storage)
        _ai_dispatcher.include_router(dev_router)
        _ai_dispatcher.include_router(router)
    return _ai_dispatcher
