import sqlite3
import asyncio
import logging
import random
import io
import json
import uuid
import time
from datetime import datetime, timedelta
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup
from telegram.ext import (
    Application, CommandHandler, MessageHandler,
    CallbackQueryHandler, filters, ContextTypes
)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# ==================== تنظیمات اصلی ====================
BOT_TOKEN = "8555977907:AAGTXkPdA1nSqufEWjgMRfw4iL_Qb7dlfIY"
BOT_USERNAME = "Shartttttbot"
ADMIN_IDS = [7390952143]

# تنظیمات جوین اجباری
FORCE_JOIN_CHANNELS = [
    {"username": "RyderRolePlay", "name": "RyderRolePlay", "link": "https://t.me/RyderRolePlay"}
]

# تنظیمات سطح‌بندی (قابل تنظیم توسط ادمین)
LEVEL_SETTINGS = {
    "base_hops": 10,
    "increment": 20,
    "max_level": 1000
}

# تنظیمات میو (Mute)
MUTE_SETTINGS = {
    "default_duration": 300,
    "max_duration": 86400,
    "spam_threshold": 3,
    "spam_window": 2,
    "spam_mute_duration": 60
}

# تنظیمات ریفرال
REFERRAL_SETTINGS = {
    "enabled": True,
    "reward_sender": 5000,
    "reward_joiner": 1000
}

# تنظیمات هپ
HOP_COOLDOWN = 300
HOP_BASE_POINTS = 50
LEVEL_THRESHOLDS = []
hops = 10
for i in range(1000):
    LEVEL_THRESHOLDS.append(hops)
    if i < 9: hops += 20
    elif i < 49: hops += 50
    elif i < 199: hops += 100
    else: hops += 200

# ==================== دیتابیس ====================
def get_db():
    conn = sqlite3.connect("happy_bot.db", timeout=10)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA synchronous=NORMAL")
    return conn

class db_conn:
    def __enter__(self):
        self.conn = get_db()
        return self.conn
    def __exit__(self, *_):
        try:
            self.conn.close()
        except Exception:
            pass

def init_db():
    conn = get_db()
    c = conn.cursor()
    
    # جدول کاربران
    c.execute("""CREATE TABLE IF NOT EXISTS users (
        user_id    INTEGER PRIMARY KEY,
        username   TEXT,
        first_name TEXT,
        hop_points REAL DEFAULT 0,
        total_hops INTEGER DEFAULT 0,
        level      INTEGER DEFAULT 1,
        last_hop   TEXT DEFAULT NULL,
        joined_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        is_muted   INTEGER DEFAULT 0,
        muted_until TEXT DEFAULT NULL
    )""")
    
    # جدول گروه‌ها
    c.execute("""CREATE TABLE IF NOT EXISTS groups (
        group_id    INTEGER PRIMARY KEY,
        title       TEXT,
        level       INTEGER DEFAULT 1,
        treasury    REAL DEFAULT 0,
        total_hops  INTEGER DEFAULT 0,
        total_dogs  INTEGER DEFAULT 0,
        total_bones INTEGER DEFAULT 0,
        total_fish  INTEGER DEFAULT 0
    )""")
    
    # جدول سگ‌ها
    c.execute("""CREATE TABLE IF NOT EXISTS dogs (
        user_id      INTEGER PRIMARY KEY,
        name         TEXT DEFAULT 'سگولو',
        level        INTEGER DEFAULT 1,
        rank         INTEGER DEFAULT 1,
        points_box   REAL DEFAULT 0,
        fed_until    TEXT DEFAULT NULL,
        last_collect TEXT DEFAULT NULL
    )""")
    
    # جدول قلاب
    c.execute("""CREATE TABLE IF NOT EXISTS hooks (
        user_id   INTEGER PRIMARY KEY,
        level     INTEGER DEFAULT 1,
        last_cast TEXT DEFAULT NULL
    )""")
    
    # جدول استخوان‌های در انتظار
    c.execute("""CREATE TABLE IF NOT EXISTS pending_bones (
        user_id   INTEGER PRIMARY KEY,
        bone_name TEXT,
        weight    REAL,
        price     INTEGER,
        caught_at TEXT
    )""")
    
    # جدول بانک
    c.execute("""CREATE TABLE IF NOT EXISTS bank (
        user_id         INTEGER PRIMARY KEY,
        balance         REAL DEFAULT 0,
        account_number  TEXT UNIQUE,
        opened_at       TEXT DEFAULT CURRENT_TIMESTAMP,
        last_interest   TEXT DEFAULT NULL,
        last_num_change TEXT DEFAULT NULL
    )""")
    
    # جدول انتقالات
    c.execute("""CREATE TABLE IF NOT EXISTS transfers (
        user_id       INTEGER PRIMARY KEY,
        last_transfer TEXT DEFAULT NULL
    )""")
    
    # جدول سگ‌های ولگرد
    c.execute("""CREATE TABLE IF NOT EXISTS stray_dogs (
        group_id     INTEGER PRIMARY KEY,
        tries_left   INTEGER DEFAULT 3,
        current_cost INTEGER DEFAULT 300,
        appeared_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        rescuer_ids  TEXT DEFAULT ''
    )""")
    
    # جدول سگ‌های نجات یافته
    c.execute("""CREATE TABLE IF NOT EXISTS user_strays (
        user_id INTEGER PRIMARY KEY,
        count   INTEGER DEFAULT 0
    )""")
    
    # جدول زندان
    c.execute("""CREATE TABLE IF NOT EXISTS jail (
        user_id     INTEGER PRIMARY KEY,
        jailed_at   TEXT,
        release_at  TEXT,
        reason      TEXT DEFAULT 'قاچاق',
        spam_count  INTEGER DEFAULT 0,
        work_points INTEGER DEFAULT 0,
        last_work   TEXT DEFAULT NULL
    )""")
    
    # جدول اسپم (برای ردیابی)
    c.execute("""CREATE TABLE IF NOT EXISTS spam_tracker (
        user_id    INTEGER,
        group_id   INTEGER,
        msg_times  TEXT DEFAULT '[]',
        jail_count INTEGER DEFAULT 0,
        PRIMARY KEY (user_id, group_id)
    )""")
    
    # جدول کارخانه
    c.execute("""CREATE TABLE IF NOT EXISTS factories (
        user_id         INTEGER PRIMARY KEY,
        level           INTEGER DEFAULT 1,
        exp             INTEGER DEFAULT 0,
        warehouse_level INTEGER DEFAULT 1,
        machine_level   INTEGER DEFAULT 1,
        stock           INTEGER DEFAULT 0,
        last_produced   TEXT DEFAULT NULL,
        producing       INTEGER DEFAULT 0,
        product_idx     INTEGER DEFAULT 0,
        production_end  TEXT DEFAULT NULL
    )""")
    
    # جدول قیمت بازار
    c.execute("""CREATE TABLE IF NOT EXISTS market_prices (
        product_idx INTEGER PRIMARY KEY,
        multiplier  REAL DEFAULT 1.0,
        updated_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول ادمین‌های فرعی
    c.execute("""CREATE TABLE IF NOT EXISTS sub_admins (
        user_id    INTEGER PRIMARY KEY,
        username   TEXT,
        first_name TEXT,
        added_by   INTEGER,
        added_at   TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول قرعه‌کشی
    c.execute("""CREATE TABLE IF NOT EXISTS lotteries (
        lottery_id   TEXT PRIMARY KEY,
        title        TEXT,
        prize        INTEGER DEFAULT 0,
        winner_count INTEGER DEFAULT 1,
        state        TEXT DEFAULT 'open',
        created_by   INTEGER,
        created_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        end_at       TEXT DEFAULT NULL
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS lottery_entries (
        lottery_id TEXT,
        user_id    INTEGER,
        username   TEXT,
        first_name TEXT,
        joined_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (lottery_id, user_id)
    )""")
    
    # جدول مارکت کاربران
    c.execute("""CREATE TABLE IF NOT EXISTS user_market (
        listing_id   TEXT PRIMARY KEY,
        seller_id    INTEGER,
        seller_name  TEXT,
        title        TEXT,
        description  TEXT,
        content      TEXT,
        price        INTEGER,
        max_buyers   INTEGER DEFAULT 1,
        buyer_count  INTEGER DEFAULT 0,
        status       TEXT DEFAULT 'pending',
        created_at   TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS user_market_buyers (
        listing_id TEXT,
        buyer_id   INTEGER,
        bought_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (listing_id, buyer_id)
    )""")
    
    # جدول تنظیمات ربات
    c.execute("""CREATE TABLE IF NOT EXISTS bot_settings (
        key   TEXT PRIMARY KEY,
        value TEXT
    )""")
    
    # جدول ریفرال
    c.execute("""CREATE TABLE IF NOT EXISTS referrals (
        user_id    INTEGER PRIMARY KEY,
        inviter_id INTEGER,
        rewarded   INTEGER DEFAULT 0,
        joined_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول کانال‌های جوین اجباری
    c.execute("""CREATE TABLE IF NOT EXISTS force_join_channels (
        username  TEXT PRIMARY KEY,
        name      TEXT,
        link      TEXT,
        added_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول شهردار
    c.execute("""CREATE TABLE IF NOT EXISTS mayor (
        group_id    INTEGER PRIMARY KEY,
        user_id     INTEGER,
        username    TEXT,
        first_name  TEXT,
        elected_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        term_end    TEXT,
        popularity  INTEGER DEFAULT 100
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_decrees (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        group_id    INTEGER,
        user_id     INTEGER,
        decree_type TEXT,
        decree_name TEXT,
        issued_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        expires_at  TEXT
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_elections (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        group_id    INTEGER,
        status      TEXT DEFAULT 'candidacy',
        started_by  INTEGER,
        started_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        ended_at    TEXT DEFAULT NULL
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_candidates (
        election_id INTEGER,
        user_id     INTEGER,
        username    TEXT,
        first_name  TEXT,
        pledges     TEXT DEFAULT '',
        joined_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (election_id, user_id)
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_election_votes (
        election_id    INTEGER,
        voter_id       INTEGER,
        candidate_id   INTEGER,
        voted_at       TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (election_id, voter_id)
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_protests (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        group_id    INTEGER,
        user_id     INTEGER,
        username    TEXT,
        first_name  TEXT,
        reason      TEXT,
        status      TEXT DEFAULT 'pending',
        created_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_protest_votes (
        protest_id  INTEGER,
        user_id     INTEGER,
        vote        TEXT,
        voted_at    TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (protest_id, user_id)
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS mayor_log (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        group_id    INTEGER,
        user_id     INTEGER,
        action_type TEXT,
        action_desc TEXT,
        created_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول بحران شهری
    c.execute("""CREATE TABLE IF NOT EXISTS city_crises (
        id           INTEGER PRIMARY KEY AUTOINCREMENT,
        group_id     INTEGER,
        crisis_type  TEXT,
        status       TEXT DEFAULT 'active',
        started_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        expires_at   TEXT,
        resolved_by  INTEGER DEFAULT NULL,
        decision     TEXT DEFAULT NULL,
        resolved_at  TEXT DEFAULT NULL
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS city_crisis_penalties (
        group_id     INTEGER PRIMARY KEY,
        penalty_type TEXT,
        penalty_value INTEGER,
        expires_at   TEXT
    )""")
    
    # جدول رهبر
    c.execute("""CREATE TABLE IF NOT EXISTS leader (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id     INTEGER UNIQUE,
        username    TEXT,
        first_name  TEXT,
        elected_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        term_end    TEXT,
        popularity  INTEGER DEFAULT 100
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_elections (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        status      TEXT DEFAULT 'candidacy',
        started_by  INTEGER,
        started_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        vote_start  TEXT DEFAULT NULL,
        ended_at    TEXT DEFAULT NULL
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_candidates (
        election_id INTEGER,
        user_id     INTEGER,
        username    TEXT,
        first_name  TEXT,
        pledges     TEXT DEFAULT '',
        joined_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (election_id, user_id)
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_votes (
        election_id INTEGER,
        voter_id    INTEGER,
        candidate_id INTEGER,
        voted_at    TEXT DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (election_id, voter_id)
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_commands (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        command_key TEXT,
        issued_at   TEXT DEFAULT CURRENT_TIMESTAMP,
        expires_at  TEXT,
        is_active   INTEGER DEFAULT 1
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_laws (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        law_key     TEXT,
        law_name    TEXT,
        enacted_at  TEXT DEFAULT CURRENT_TIMESTAMP,
        expires_at  TEXT,
        is_active   INTEGER DEFAULT 1
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS national_treasury (
        id      INTEGER PRIMARY KEY CHECK(id=1),
        balance REAL DEFAULT 0
    )""")
    
    c.execute("INSERT OR IGNORE INTO national_treasury (id,balance) VALUES (1,0)")
    
    c.execute("""CREATE TABLE IF NOT EXISTS treasury_log (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        amount      REAL,
        reason      TEXT,
        user_id     INTEGER,
        created_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    c.execute("""CREATE TABLE IF NOT EXISTS leader_log (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id     INTEGER,
        action_type TEXT,
        action_desc TEXT,
        created_at  TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول تراکنش‌ها
    c.execute("""CREATE TABLE IF NOT EXISTS transactions (
        id         INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id    INTEGER,
        type       TEXT,
        amount     REAL,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP
    )""")
    
    # جدول میو (Mute)
    c.execute("""CREATE TABLE IF NOT EXISTS mutes (
        user_id     INTEGER,
        group_id    INTEGER,
        muted_at    TEXT DEFAULT CURRENT_TIMESTAMP,
        muted_until TEXT,
        reason      TEXT DEFAULT 'اسپم',
        muted_by    INTEGER,
        PRIMARY KEY (user_id, group_id)
    )""")
    
    conn.commit()
    
    # اضافه کردن ستون‌های جدید در صورت نیاز
    for table, col, definition in [
        ("users", "is_muted", "INTEGER DEFAULT 0"),
        ("users", "muted_until", "TEXT DEFAULT NULL"),
    ]:
        try:
            conn.execute(f"ALTER TABLE {table} ADD COLUMN {col} {definition}")
            conn.commit()
        except Exception:
            pass
    
    conn.close()
    logger.info("✅ دیتابیس آماده شد")

# ==================== توابع کمکی ====================

def cbtn(text: str, callback_data: str = None, url: str = None, **kwargs) -> InlineKeyboardButton:
    build_kwargs = {"text": text}
    if callback_data is not None:
        build_kwargs["callback_data"] = callback_data
    if url is not None:
        build_kwargs["url"] = url
    build_kwargs.update(kwargs)
    return InlineKeyboardButton(**build_kwargs)

def is_admin(user_id: int) -> bool:
    return user_id in ADMIN_IDS

def is_sub_admin(user_id: int) -> bool:
    with db_conn() as conn:
        return conn.execute("SELECT 1 FROM sub_admins WHERE user_id=?", (user_id,)).fetchone() is not None

def is_any_admin(user_id: int) -> bool:
    return is_admin(user_id) or is_sub_admin(user_id)

def get_user(user_id):
    with db_conn() as conn:
        return conn.execute("SELECT * FROM users WHERE user_id=?", (user_id,)).fetchone()

def ensure_user(user_id, username, first_name):
    with db_conn() as conn:
        conn.execute("INSERT OR IGNORE INTO users (user_id,username,first_name) VALUES (?,?,?)",
                     (user_id, username, first_name))
        conn.execute("UPDATE users SET username=?,first_name=? WHERE user_id=?",
                     (username, first_name, user_id))
        conn.commit()

def ensure_group(group_id, title):
    with db_conn() as conn:
        conn.execute("INSERT OR IGNORE INTO groups (group_id,title) VALUES (?,?)", (group_id, title))
        conn.execute("UPDATE groups SET title=? WHERE group_id=?", (title, group_id))
        conn.commit()

def get_level(total_hops):
    level = 1
    for i, threshold in enumerate(LEVEL_THRESHOLDS):
        if total_hops >= threshold:
            level = i + 2
        else:
            break
    return min(level, LEVEL_SETTINGS["max_level"])

def hops_for_next_level(level):
    if level >= LEVEL_SETTINGS["max_level"]:
        return 0
    return LEVEL_THRESHOLDS[level - 1]

def calc_hop_reward(level):
    _base = 56.74
    _ratio = 1.2336
    max_reward = int(_base * (_ratio ** (level - 1)))
    min_reward = 20
    if level >= 43:
        mid = int(max_reward * 0.60)
        if random.random() < 0.60:
            return random.randint(min_reward, mid)
        else:
            return random.randint(mid, max_reward)
    return random.randint(min_reward, max_reward)

def parse_amount(text):
    text = text.strip().replace(",", "").replace("_", "")
    multipliers = {"k": 1_000, "کی": 1_000, "کا": 1_000, "m": 1_000_000, "میل": 1_000_000}
    for suffix, mult in multipliers.items():
        if text.lower().endswith(suffix):
            try:
                return int(float(text[:-len(suffix)]) * mult)
            except:
                return -1
    try:
        return int(float(text))
    except:
        return -1

def is_in_jail(user_id):
    with db_conn() as conn:
        row = conn.execute("SELECT * FROM jail WHERE user_id=?", (user_id,)).fetchone()
        if not row:
            return False, None
        if datetime.fromisoformat(row["release_at"]) > datetime.now():
            return True, row
        conn.execute("DELETE FROM jail WHERE user_id=?", (user_id,))
        conn.commit()
    return False, None

def jail_user(user_id, reason="قاچاق", duration_seconds=None):
    now = datetime.now()
    secs = duration_seconds if duration_seconds else 1800
    release = now + timedelta(seconds=secs)
    with db_conn() as conn:
        conn.execute(
            "INSERT OR REPLACE INTO jail (user_id,jailed_at,release_at,reason,spam_count,work_points,last_work) VALUES (?,?,?,?,0,0,NULL)",
            (user_id, now.isoformat(), release.isoformat(), reason)
        )
        conn.commit()

def is_muted(user_id, group_id):
    with db_conn() as conn:
        row = conn.execute(
            "SELECT * FROM mutes WHERE user_id=? AND group_id=? AND muted_until > datetime('now')",
            (user_id, group_id)
        ).fetchone()
        if row:
            return True, row
        conn.execute("DELETE FROM mutes WHERE user_id=? AND group_id=?", (user_id, group_id))
        conn.commit()
    return False, None

def mute_user(user_id, group_id, duration_seconds, reason="اسپم", muted_by=0):
    now = datetime.now()
    muted_until = now + timedelta(seconds=duration_seconds)
    with db_conn() as conn:
        conn.execute(
            "INSERT OR REPLACE INTO mutes (user_id, group_id, muted_at, muted_until, reason, muted_by) VALUES (?,?,?,?,?,?)",
            (user_id, group_id, now.isoformat(), muted_until.isoformat(), reason, muted_by)
        )
        conn.commit()

def unmute_user(user_id, group_id):
    with db_conn() as conn:
        conn.execute("DELETE FROM mutes WHERE user_id=? AND group_id=?", (user_id, group_id))
        conn.commit()

# ==================== توابع جوین اجباری ====================

def get_force_join_channels():
    channels = list(FORCE_JOIN_CHANNELS)
    try:
        with db_conn() as conn:
            rows = conn.execute(
                "SELECT username, name, link FROM force_join_channels ORDER BY added_at"
            ).fetchall()
            for r in rows:
                channels.append({"username": r["username"], "name": r["name"], "link": r["link"]})
    except Exception:
        pass
    return channels

def add_force_join_channel(username, name, link):
    with db_conn() as conn:
        conn.execute(
            "INSERT OR REPLACE INTO force_join_channels (username, name, link) VALUES (?,?,?)",
            (username, name, link)
        )
        conn.commit()

def remove_force_join_channel(username):
    with db_conn() as conn:
        conn.execute("DELETE FROM force_join_channels WHERE username=?", (username,))
        conn.commit()

async def is_member_of_all_channels(bot, user_id):
    missing = []
    for ch in get_force_join_channels():
        try:
            member = await bot.get_chat_member(f"@{ch['username']}", user_id)
            if member.status not in ("member", "administrator", "creator"):
                missing.append(ch)
        except Exception:
            pass
    return (len(missing) == 0), missing

def build_join_keyboard(missing_channels=None):
    channels = missing_channels or get_force_join_channels()
    rows = [[cbtn(f"عضویت در {ch['name']} 🔔", url=ch["link"])] for ch in channels]
    rows.append([cbtn("✅ عضو شدم، بررسی کن!", callback_data="check_join")])
    return InlineKeyboardMarkup(rows)

async def force_join_check(update, context):
    if not update.message:
        return False
    if update.effective_chat.type not in ("group", "supergroup"):
        return False
    user = update.effective_user
    if not user:
        return False
    if is_admin(user.id):
        return False
    ok, missing = await is_member_of_all_channels(context.bot, user.id)
    if not ok:
        ch_list = "\n".join(f"• {ch['name']}" for ch in missing)
        text = (
            f"⛔️ {user.mention_html()} عزیز!\n\n"
            f"برای استفاده از ربات هاپ‌داگ، ابتدا باید عضو این کانال‌ها بشی:\n\n"
            f"{ch_list}\n\n"
            f"👆 روی دکمه‌ها کلیک کن، عضو بشو، بعد «عضو شدم» رو بزن:"
        )
        await update.message.reply_text(
            text,
            reply_markup=build_join_keyboard(missing),
            parse_mode="HTML"
        )
        return True
    return False

async def check_join_callback(update, context):
    query = update.callback_query
    user = query.from_user
    await query.answer()
    ok, missing = await is_member_of_all_channels(context.bot, user.id)
    if ok:
        await query.edit_message_text(
            f"✅ {user.mention_html()} خوش اومدی به هاپ‌داگ! 🐾\nالان می‌تونی از ربات استفاده کنی 🎉",
            parse_mode="HTML"
        )
    else:
        ch_names = "، ".join(ch["name"] for ch in missing)
        await query.answer(
            f"❌ هنوز عضو {ch_names} نشدی!\nاول عضو بشو بعد دکمه رو بزن.",
            show_alert=True
        )

# ==================== توابع ضد اسپم ====================
# برای ردیابی پیام‌های کاربران در حافظه (برای سرعت)
spam_tracker = {}

def check_spam(user_id, group_id):
    key = (user_id, group_id)
    now = time.time()
    window = MUTE_SETTINGS["spam_window"]
    threshold = MUTE_SETTINGS["spam_threshold"]
    
    if key not in spam_tracker:
        spam_tracker[key] = []
    
    # حذف زمان‌های قدیمی
    spam_tracker[key] = [t for t in spam_tracker[key] if now - t < window]
    spam_tracker[key].append(now)
    
    if len(spam_tracker[key]) > threshold:
        duration = MUTE_SETTINGS["spam_mute_duration"]
        mute_user(user_id, group_id, duration, "اسپم خودکار", 0)
        spam_tracker[key] = []
        return True, duration
    return False, 0

# ==================== توابع پنل ادمین ====================

async def admin_panel(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    if not is_admin(user.id):
        return  # هیچ پاسخی داده نمی‌شه
    
    with db_conn() as conn:
        total_users = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
        total_groups = conn.execute("SELECT COUNT(*) FROM groups").fetchone()[0]
        total_hops = conn.execute("SELECT SUM(total_hops) FROM users").fetchone()[0] or 0
        total_points = conn.execute("SELECT SUM(hop_points) FROM users").fetchone()[0] or 0
        jailed = conn.execute("SELECT COUNT(*) FROM jail WHERE release_at > datetime('now')").fetchone()[0]
        muted = conn.execute("SELECT COUNT(*) FROM mutes WHERE muted_until > datetime('now')").fetchone()[0]
    
    kb = InlineKeyboardMarkup([
        [cbtn("👥 آمار کاربران", "adm_stats_users"),
         cbtn("📊 آمار گروه‌ها", "adm_stats_groups")],
        [cbtn("🔇 مدیریت میو", "adm_mute_menu"),
         cbtn("🔑 مدیریت ادمین‌ها", "adm_admins_menu")],
        [cbtn("📈 تنظیمات سطح‌بندی", "adm_level_settings"),
         cbtn("📢 تنظیمات جوین", "adm_join_settings")],
        [cbtn("💰 مدیریت ریفرال", "adm_ref_menu"),
         cbtn("🔄 ریست کاربران", "adm_reset_menu")],
        [cbtn("🛡️ تنظیمات ضد اسپم", "adm_antispam_settings"),
         cbtn("📋 گزارش کامل", "adm_full_report")]
    ])
    
    text = (
        f"👑 *پنل مدیریت ربات هاپ‌داگ*\n\n"
        f"👥 کاربران کل: {total_users:,}\n"
        f"🏠 گروه‌ها: {total_groups:,}\n"
        f"🐾 کل هاپ‌ها: {total_hops:,}\n"
        f"💰 کل پوینت‌ها: {total_points:,.0f}\n"
        f"⛓️ زندانی‌ها: {jailed}\n"
        f"🔇 میو شده‌ها: {muted}\n\n"
        f"یک گزینه انتخاب کن:"
    )
    await update.message.reply_text(text, parse_mode="Markdown", reply_markup=kb)

async def admin_stats_users(update, context):
    query = update.callback_query
    await query.answer()
    with db_conn() as conn:
        top = conn.execute(
            "SELECT first_name, hop_points, level, total_hops FROM users ORDER BY hop_points DESC LIMIT 10"
        ).fetchall()
        total = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
    
    text = f"👥 *۱۰ کاربر برتر از نظر پوینت*\n\n"
    medals = ["🥇", "🥈", "🥉"]
    for i, u in enumerate(top):
        medal = medals[i] if i < 3 else f"{i+1}."
        text += f"{medal} {u['first_name']} — {u['hop_points']:,.0f} 🐾 | سطح {u['level']} | {u['total_hops']} هاپ\n"
    text += f"\n📊 کل کاربران: {total:,}"
    
    await query.edit_message_text(
        text,
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_back")]])
    )

async def admin_stats_groups(update, context):
    query = update.callback_query
    await query.answer()
    with db_conn() as conn:
        top = conn.execute(
            "SELECT title, treasury, total_hops FROM groups ORDER BY treasury DESC LIMIT 10"
        ).fetchall()
        total = conn.execute("SELECT COUNT(*) FROM groups").fetchone()[0]
    
    text = f"🏠 *۱۰ گروه برتر از نظر خزانه*\n\n"
    medals = ["🥇", "🥈", "🥉"]
    for i, g in enumerate(top):
        medal = medals[i] if i < 3 else f"{i+1}."
        text += f"{medal} {g['title'] or 'گروه ناشناس'} — {g['treasury']:,.0f} 🐾 | {g['total_hops']} هاپ\n"
    text += f"\n📊 کل گروه‌ها: {total:,}"
    
    await query.edit_message_text(
        text,
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_back")]])
    )

async def admin_full_report(update, context):
    query = update.callback_query
    await query.answer()
    with db_conn() as conn:
        total_users = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
        total_groups = conn.execute("SELECT COUNT(*) FROM groups").fetchone()[0]
        total_hops = conn.execute("SELECT SUM(total_hops) FROM users").fetchone()[0] or 0
        total_points = conn.execute("SELECT SUM(hop_points) FROM users").fetchone()[0] or 0
        jailed = conn.execute("SELECT COUNT(*) FROM jail WHERE release_at > datetime('now')").fetchone()[0]
        muted = conn.execute("SELECT COUNT(*) FROM mutes WHERE muted_until > datetime('now')").fetchone()[0]
        total_dogs = conn.execute("SELECT COUNT(*) FROM dogs").fetchone()[0]
        total_bones = conn.execute("SELECT SUM(total_bones) FROM groups").fetchone()[0] or 0
        total_fish = conn.execute("SELECT SUM(total_fish) FROM groups").fetchone()[0] or 0
        bank_total = conn.execute("SELECT SUM(balance) FROM bank").fetchone()[0] or 0
        treasury_total = conn.execute("SELECT SUM(treasury) FROM groups").fetchone()[0] or 0
    
    text = (
        f"📋 *گزارش کامل ربات هاپ‌داگ*\n\n"
        f"👥 کاربران: {total_users:,}\n"
        f"🏠 گروه‌ها: {total_groups:,}\n"
        f"🐾 کل هاپ‌ها: {total_hops:,}\n"
        f"💰 کل پوینت‌ها: {total_points:,.0f}\n"
        f"🐕 سگ‌ها: {total_dogs:,}\n"
        f"🦴 استخوان‌ها: {total_bones:,}\n"
        f"🐟 ماهی‌ها: {total_fish:,}\n"
        f"🏦 پول بانک‌ها: {bank_total:,.0f}\n"
        f"🏰 خزانه گروه‌ها: {treasury_total:,.0f}\n"
        f"⛓️ زندانی‌ها: {jailed}\n"
        f"🔇 میو شده‌ها: {muted}\n"
    )
    
    await query.edit_message_text(
        text,
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_back")]])
    )

# ==================== توابع مدیریت میو ====================

async def admin_mute_menu(update, context):
    query = update.callback_query
    await query.answer()
    
    kb = InlineKeyboardMarkup([
        [cbtn("🔇 میو کردن کاربر", "adm_mute_user")],
        [cbtn("🔊 آنمیو کردن کاربر", "adm_unmute_user")],
        [cbtn("📋 لیست میو شده‌ها", "adm_muted_list")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    await query.edit_message_text(
        "🔇 *مدیریت میو (Mute)*\n\n"
        "کاربر مورد نظر رو انتخاب کن:",
        parse_mode="Markdown",
        reply_markup=kb
    )

async def admin_mute_user(update, context):
    query = update.callback_query
    await query.answer()
    context.user_data["admin_action"] = "mute"
    await query.edit_message_text(
        "🔇 *میو کردن کاربر*\n\n"
        "روی پیام کاربر مورد نظر ریپلای کن و بنویس:\n"
        "`/mute [مدت به دقیقه] [دلیل]`\n\n"
        "مثال: `/mute 30 اسپم`\n\n"
        "برای لغو بنویس: `/cancel`",
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_mute_menu")]])
    )

async def admin_unmute_user(update, context):
    query = update.callback_query
    await query.answer()
    context.user_data["admin_action"] = "unmute"
    await query.edit_message_text(
        "🔊 *آنمیو کردن کاربر*\n\n"
        "روی پیام کاربر مورد نظر ریپلای کن و بنویس:\n"
        "`/unmute`\n\n"
        "برای لغو بنویس: `/cancel`",
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_mute_menu")]])
    )

async def admin_muted_list(update, context):
    query = update.callback_query
    await query.answer()
    with db_conn() as conn:
        muted = conn.execute(
            "SELECT * FROM mutes WHERE muted_until > datetime('now') ORDER BY muted_until DESC LIMIT 20"
        ).fetchall()
    
    if not muted:
        text = "✅ هیچ کاربر میو شده‌ای وجود ندارد!"
    else:
        text = "🔇 *لیست کاربران میو شده*\n\n"
        for m in muted:
            until = datetime.fromisoformat(m["muted_until"])
            remaining = int((until - datetime.now()).total_seconds() / 60)
            text += f"• کاربر {m['user_id']} — {remaining} دقیقه مونده\n"
            text += f"  دلیل: {m['reason']}\n\n"
    
    await query.edit_message_text(
        text,
        parse_mode="Markdown",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "adm_mute_menu")]])
    )

async def handle_mute_command(update, context):
    user = update.effective_user
    if not is_admin(user.id):
        return
    
    if not update.message.reply_to_message:
        await update.message.reply_text("❌ روی پیام کاربر مورد نظر ریپلای کن!")
        return
    
    target = update.message.reply_to_message.from_user
    if target.id in ADMIN_IDS:
        await update.message.reply_text("❌ نمی‌تونی ادمین اصلی رو میو کنی!")
        return
    
    chat = update.effective_chat
    parts = update.message.text.strip().split()
    
    if len(parts) < 2:
        duration = MUTE_SETTINGS["default_duration"]
        reason = "بدون دلیل"
    else:
        try:
            duration = int(parts[1]) * 60
            if duration > MUTE_SETTINGS["max_duration"]:
                duration = MUTE_SETTINGS["max_duration"]
            reason = " ".join(parts[2:]) if len(parts) > 2 else "بدون دلیل"
        except ValueError:
            duration = MUTE_SETTINGS["default_duration"]
            reason = " ".join(parts[1:]) if len(parts) > 1 else "بدون دلیل"
    
    mute_user(target.id, chat.id, duration, reason, user.id)
    
    minutes = duration // 60
    await update.message.reply_text(
        f"🔇 *{target.first_name} به مدت {minutes} دقیقه میو شد!*\n"
        f"📌 دلیل: {reason}",
        parse_mode="Markdown"
    )

async def handle_unmute_command(update, context):
    user = update.effective_user
    if not is_admin(user.id):
        return
    
    if not update.message.reply_to_message:
        await update.message.reply_text("❌ روی پیام کاربر مورد نظر ریپلای کن!")
        return
    
    target = update.message.reply_to_message.from_user
    chat = update.effective_chat
    
    unmute_user(target.id, chat.id)
    await update.message.reply_text(f"🔊 *{target.first_name} آنمیو شد!*", parse_mode="Markdown")

# ==================== توابع تنظیمات سطح‌بندی ====================

async def admin_level_settings(update, context):
    query = update.callback_query
    await query.answer()
    
    kb = InlineKeyboardMarkup([
        [cbtn(f"📊 هاپ پایه: {LEVEL_SETTINGS['base_hops']}", "adm_level_base")],
        [cbtn(f"📈 افزایش هر سطح: {LEVEL_SETTINGS['increment']}", "adm_level_inc")],
        [cbtn(f"🎯 حداکثر سطح: {LEVEL_SETTINGS['max_level']}", "adm_level_max")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    text = (
        f"📈 *تنظیمات سطح‌بندی*\n\n"
        f"هاپ پایه: {LEVEL_SETTINGS['base_hops']}\n"
        f"افزایش هر سطح: {LEVEL_SETTINGS['increment']}\n"
        f"حداکثر سطح: {LEVEL_SETTINGS['max_level']}\n\n"
        f"برای تغییر هر مقدار، روی دکمه مربوطه کلیک کن:"
    )
    await query.edit_message_text(text, parse_mode="Markdown", reply_markup=kb)

async def admin_join_settings(update, context):
    query = update.callback_query
    await query.answer()
    
    channels = get_force_join_channels()
    ch_list = ""
    for i, ch in enumerate(channels, 1):
        ch_list += f"{i}. {ch['name']} (@{ch['username']})\n"
    
    kb = InlineKeyboardMarkup([
        [cbtn("➕ افزودن کانال", "adm_join_add")],
        [cbtn("➖ حذف کانال", "adm_join_remove")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    text = (
        f"📢 *تنظیمات جوین اجباری*\n\n"
        f"کانال‌های فعلی:\n{ch_list or 'هیچ کانالی ثبت نشده!'}\n\n"
        f"برای افزودن یا حذف کانال از دکمه‌ها استفاده کن:"
    )
    await query.edit_message_text(text, parse_mode="Markdown", reply_markup=kb)

async def admin_ref_menu(update, context):
    query = update.callback_query
    await query.answer()
    
    enabled = REFERRAL_SETTINGS["enabled"]
    status = "✅ فعال" if enabled else "❌ غیرفعال"
    
    kb = InlineKeyboardMarkup([
        [cbtn(f"🔀 وضعیت: {status}", "adm_ref_toggle")],
        [cbtn(f"💰 جایزه دعوت‌کننده: {REFERRAL_SETTINGS['reward_sender']:,}", "adm_ref_sender")],
        [cbtn(f"💰 جایزه دعوت‌شونده: {REFERRAL_SETTINGS['reward_joiner']:,}", "adm_ref_joiner")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    text = (
        f"💰 *مدیریت سیستم ریفرال*\n\n"
        f"وضعیت: {status}\n"
        f"جایزه دعوت‌کننده: {REFERRAL_SETTINGS['reward_sender']:,}\n"
        f"جایزه دعوت‌شونده: {REFERRAL_SETTINGS['reward_joiner']:,}\n\n"
        f"برای تغییر هر مقدار، روی دکمه مربوطه کلیک کن:"
    )
    await query.edit_message_text(text, parse_mode="Markdown", reply_markup=kb)

async def admin_reset_menu(update, context):
    query = update.callback_query
    await query.answer()
    
    kb = InlineKeyboardMarkup([
        [cbtn("🔄 ریست بر اساس موجودی", "adm_reset_by_balance")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    text = (
        f"🔄 *ریست کاربران*\n\n"
        f"⚠️ این عملیات برگشت‌پذیر نیست!\n"
        f"کاربرانی که موجودی‌شان از حد مشخصی بیشتر باشد، ریست می‌شوند.\n\n"
        f"یک گزینه انتخاب کن:"
    )
    await query.edit_message_text(text, parse_mode="Markdown", reply_markup=kb)

# ==================== توابع ضد اسپم در پنل ====================

async def admin_antispam_settings(update, context):
    query = update.callback_query
    await query.answer()
    
    kb = InlineKeyboardMarkup([
        [cbtn(f"📊 تعداد پیام مجاز: {MUTE_SETTINGS['spam_threshold']}", "adm_antispam_threshold")],
        [cbtn(f"⏱️ بازه زمانی (ثانیه): {MUTE_SETTINGS['spam_window']}", "adm_antispam_window")],
        [cbtn(f"🔇 مدت میو اسپم (ثانیه): {MUTE_SETTINGS['spam_mute_duration']}", "adm_antispam_duration")],
        [cbtn("🔙 بازگشت", "adm_back")]
    ])
    
    text = (
        f"🛡️ *تنظیمات ضد اسپم*\n\n"
        f"تعداد پیام مجاز: {MUTE_SETTINGS['spam_threshold']}\n"
        f"بازه زمانی (ثانیه): {MUTE_SETTINGS['spam_window']}\n"
        f"مدت میو اسپم (ثانیه): {MUTE_SETTINGS['spam_mute_duration']}\n\n"
        f"برای تغییر هر مقدار، روی دکمه مربوطه کلیک کن:"
    )
    await query.edit_message_text(text, parse_mode="Markdown", reply_markup=kb)

async def admin_antispam_threshold(update, context):
    query = update.callback_query
    await query.answer()
    context.user_data["admin_setting"] = "antispam_threshold"
    await query.edit_message_text(
        "🔢 تعداد پیام مجاز در بازه زمانی را وارد کن:\n(عدد صحیح مثبت)",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 انصراف", "adm_antispam_settings")]])
    )

async def admin_antispam_window(update, context):
    query = update.callback_query
    await query.answer()
    context.user_data["admin_setting"] = "antispam_window"
    await query.edit_message_text(
        "⏱️ بازه زمانی بر حسب ثانیه را وارد کن:\n(عدد صحیح مثبت)",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 انصراف", "adm_antispam_settings")]])
    )

async def admin_antispam_duration(update, context):
    query = update.callback_query
    await query.answer()
    context.user_data["admin_setting"] = "antispam_duration"
    await query.edit_message_text(
        "🔇 مدت میو به دلیل اسپم بر حسب ثانیه را وارد کن:\n(عدد صحیح مثبت)",
        reply_markup=InlineKeyboardMarkup([[cbtn("🔙 انصراف", "adm_antispam_settings")]])
    )

async def handle_antispam_text_input(update, context):
    user = update.effective_user
    if not is_admin(user.id):
        return False
    setting = context.user_data.get("admin_setting")
    if not setting:
        return False
    
    text = update.message.text.strip()
    try:
        value = int(text)
        if value <= 0:
            raise ValueError
    except ValueError:
        await update.message.reply_text("❌ لطفاً یک عدد صحیح مثبت وارد کن.")
        return True
    
    if setting == "antispam_threshold":
        MUTE_SETTINGS["spam_threshold"] = value
        await update.message.reply_text(f"✅ تعداد پیام مجاز به {value} تغییر کرد.")
    elif setting == "antispam_window":
        MUTE_SETTINGS["spam_window"] = value
        await update.message.reply_text(f"✅ بازه زمانی به {value} ثانیه تغییر کرد.")
    elif setting == "antispam_duration":
        MUTE_SETTINGS["spam_mute_duration"] = value
        await update.message.reply_text(f"✅ مدت میو اسپم به {value} ثانیه تغییر کرد.")
    
    context.user_data.pop("admin_setting", None)
    return True

# ==================== هندلرهای اصلی ====================

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    chat = update.effective_chat

    if chat.type == "private":
        ensure_user(user.id, user.username or "", user.first_name)

        keyboard = ReplyKeyboardMarkup([
            ["🎁 دعوت دوستان", "🐾 هاپوهام"],
            ["🛒 مارکت", "📖 راهنما"],
            ["📊 لیدربورد"]
        ], resize_keyboard=True)

        await update.message.reply_text(
            f"🐕 سلام {user.first_name} عزیز!\n\n"
            f"من ربات هاپی هستم 🦴\n\n"
            f"📌 دستورات اصلی فقط تو گروه کار می‌کنن — منو به گروهت اضافه کن!\n\n"
            f"👇 از دکمه‌های زیر استفاده کن:",
            parse_mode="Markdown",
            reply_markup=keyboard
        )
        return

    ensure_user(user.id, user.username or "", user.first_name)
    ensure_group(chat.id, chat.title or "")
    await update.message.reply_text(
        "🐕 *ربات هاپی* اینجاست!\n\n🦴 تو گروه بنویس *هاپ* تا هاپ پوینت بگیری!\n"
        "هر ۵ دقیقه یه بار می‌تونی هاپ کنی 🐾\n\n📖 دستورات:\n"
        "پروفایل — پروفایلت\nبرترین — لیدربورد\nراهنما — راهنما",
        parse_mode="Markdown")

async def handle_hop(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    chat = update.effective_chat
    
    if chat.type == "private":
        await update.message.reply_text("🐕 هاپ فقط تو گروه کار می‌کنه!\nمنو به گروهت اضافه کن 👉 @{BOT_USERNAME}")
        return
    
    # بررسی جوین اجباری
    if await force_join_check(update, context):
        return
    
    # بررسی ضد اسپم
    is_spam, duration = check_spam(user.id, chat.id)
    if is_spam:
        await update.message.reply_text(
            f"🔇 *{user.first_name} به دلیل اسپم {duration//60} دقیقه میو شد!*",
            parse_mode="Markdown"
        )
        return
    
    ensure_user(user.id, user.username or "", user.first_name)
    ensure_group(chat.id, chat.title or "")

    # بررسی زندان
    jailed, jail_row = is_in_jail(user.id)
    if jailed:
        rel = datetime.fromisoformat(jail_row["release_at"])
        left = max(0, int((rel - datetime.now()).total_seconds()))
        m, s = divmod(left, 60)
        await update.message.reply_text(
            f"⛓️ *{user.first_name}، تو زندانی هستی!*\n"
            f"📌 دلیل: {jail_row['reason']}\n"
            f"⏱️ {m} دقیقه و {s} ثانیه تا آزادی\n\n"
            f"بنویس *زندان* تا گزینه‌های آزادی رو ببینی.",
            parse_mode="Markdown"
        )
        return
    
    # بررسی میو
    muted, mute_row = is_muted(user.id, chat.id)
    if muted:
        until = datetime.fromisoformat(mute_row["muted_until"])
        left = max(0, int((until - datetime.now()).total_seconds()))
        m, s = divmod(left, 60)
        await update.message.reply_text(
            f"🔇 *{user.first_name}، میو شدی!*\n"
            f"📌 دلیل: {mute_row['reason']}\n"
            f"⏱️ {m} دقیقه و {s} ثانیه مونده",
            parse_mode="Markdown"
        )
        return

    conn = get_db()
    u = conn.execute("SELECT * FROM users WHERE user_id=?", (user.id,)).fetchone()
    now = datetime.now()
    
    if u["last_hop"]:
        diff = (now - datetime.fromisoformat(u["last_hop"])).total_seconds()
        if diff < HOP_COOLDOWN:
            remaining = int(HOP_COOLDOWN - diff)
            m, s = divmod(remaining, 60)
            conn.close()
            await update.message.reply_text(f"⏳ {user.first_name}، هنوز باید صبر کنی!\n⏱️ {m} دقیقه و {s} ثانیه دیگه می‌تونی هاپ کنی 🐾")
            return
    
    current_level = u["level"]
    reward = calc_hop_reward(current_level)
    new_hops = u["total_hops"] + 1
    new_points = u["hop_points"] + reward
    new_level = get_level(new_hops)
    leveled_up = new_level > current_level
    
    conn.execute("UPDATE users SET hop_points=?,total_hops=?,level=?,last_hop=? WHERE user_id=?",
                 (new_points, new_hops, new_level, now.isoformat(), user.id))
    conn.execute("UPDATE groups SET total_hops=total_hops+1 WHERE group_id=?", (chat.id,))
    conn.commit()
    conn.close()
    
    next_lvl_hops = hops_for_next_level(new_level)
    progress = f"{new_hops}/{next_lvl_hops}" if new_level < LEVEL_SETTINGS["max_level"] else "MAX"
    
    msg = (f"🐕 *هاپ هاپ!* {user.first_name}\n\n🦴 +{reward:,} هاپ پوینت\n"
           f"💰 موجودی: {new_points:,.0f}\n⭐️ سطح: {new_level} | هاپ: {progress}")
    if leveled_up:
        msg += f"\n\n🎉 *لِول آپ!* به سطح {new_level} رسیدی!"
    
    await update.message.reply_text(msg, parse_mode="Markdown")

async def profile_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if await force_join_check(update, context):
        return
    await hapoha_profile(update, context)

async def hapoha_profile(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    conn = get_db()

    u = conn.execute("SELECT * FROM users WHERE user_id=?", (user.id,)).fetchone()
    if not u:
        conn.close()
        await update.message.reply_text("🐾 هنوز هاپو نزدی! اول بنویس *هاپ* تا ثبت بشی!", parse_mode="Markdown")
        return

    dog = conn.execute("SELECT * FROM dogs WHERE user_id=?", (user.id,)).fetchone()
    hook = conn.execute("SELECT * FROM hooks WHERE user_id=?", (user.id,)).fetchone()
    strays = conn.execute("SELECT count FROM user_strays WHERE user_id=?", (user.id,)).fetchone()
    jailed, jail_row = is_in_jail(user.id)

    r_pts = conn.execute("SELECT COUNT(*)+1 FROM users WHERE hop_points > ?", (u["hop_points"],)).fetchone()[0]
    r_hops = conn.execute("SELECT COUNT(*)+1 FROM users WHERE total_hops > ?", (u["total_hops"],)).fetchone()[0]
    stray_count = strays["count"] if strays else 0
    r_stray = conn.execute("SELECT COUNT(*)+1 FROM user_strays WHERE count > ?", (stray_count,)).fetchone()[0]
    conn.close()

    lvl = u["level"]
    hops = u["total_hops"]
    next_hops = hops_for_next_level(lvl)
    if next_hops > 0 and lvl > 1:
        prev_hops = hops_for_next_level(lvl - 1)
        progress = hops - prev_hops
        needed = next_hops - prev_hops
        filled = int((progress / needed) * 5) if needed > 0 else 5
    else:
        filled, needed, progress = 5, 0, hops
    bar = "█" * filled + "░" * (5 - filled)

    dog_line = ""
    if dog:
        dog_line = f"\n┐─ 🐕 سگ : {dog['name']}\n└─ 🎖️ سطح {dog['level']} | مقام {dog['rank']}"

    hook_line = ""
    if hook:
        hook_line = f"\n└─ 🎣 قلاب : سطح {hook['level']}"

    jail_line = ""
    if jailed:
        release = datetime.fromisoformat(jail_row["release_at"])
        mins = int((release - datetime.now()).total_seconds() // 60)
        jail_line = f"\n\n⛓️ *در زندان!* — {mins} دقیقه تا آزادی"

    display_name = f"@{user.username}" if user.username else user.first_name

    caption = (
        f"╔═══「 🐾 پروفایل هاپو 🐾 」\n\n"
        f"┐─ 👤 کاربر : {display_name}\n"
        f"└─ 🆔 آیدی : `{user.id}`\n\n"
        f"┐─ 💰 هاپ پوینت : {u['hop_points']:,.0f} 🦴\n"
        f"└─ 🎖️ رتبه ({r_pts:,})\n"
        f"┐─ 🐾 هاپ‌های کل : {hops:,}\n"
        f"└─ 🎖️ رتبه ({r_hops:,})\n\n"
        f"┐─ 🐶 پیشی‌های خیابونی : {stray_count}\n"
        f"└─ 🎖️ رتبه ({r_stray:,})\n"
        f"{dog_line}"
        f"{hook_line}\n\n"
        f"╚═══ ⭐️ سطح : {lvl} | {progress} / {needed if needed else '∞'} {bar}"
        f"{jail_line}"
    )

    photo_buf = None
    try:
        photos = await context.bot.get_user_profile_photos(user.id, limit=1)
        if photos.total_count > 0:
            file = await photos.photos[0][-1].get_file()
            photo_buf = io.BytesIO()
            await file.download_to_memory(photo_buf)
            photo_buf.seek(0)
    except Exception:
        pass

    if photo_buf:
        await update.message.reply_photo(photo=photo_buf, caption=caption, parse_mode="Markdown")
    else:
        await update.message.reply_text(caption, parse_mode="Markdown")

async def top_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if await force_join_check(update, context):
        return
    conn = get_db()
    rows = conn.execute(
        "SELECT first_name, hop_points, level FROM users ORDER BY hop_points DESC LIMIT 10"
    ).fetchall()
    conn.close()
    if not rows:
        await update.message.reply_text("هیچکس هاپو نزده!")
        return
    text = "🏆 *برترین هاپوها* 🐾\n\n"
    medals = ["🥇", "🥈", "🥉"]
    for i, r in enumerate(rows):
        medal = medals[i] if i < 3 else f"{i+1}."
        text += f"{medal} {r['first_name']} — {r['hop_points']:,.0f} 🦴 | سطح {r['level']}\n"
    await update.message.reply_text(text, parse_mode="Markdown")

async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if await force_join_check(update, context):
        return
    text = (
        "╔═══「 🐾 راهنمای هاپو 🐾 」\n\n"
        "┐─ 🐾 *هاپ* — جمع پوینت (کولدآون ۵ دقیقه)\n"
        "┐─ 🐕 *سگ* — خرید و مدیریت سگ\n"
        "┐─ 🎣 *قلاب* — خرید قلاب ماهیگیری\n"
        "┐─ 🦴 *استخوان* — صید استخوان\n"
        "┐─ 🏦 *بانک* — مدیریت حساب بانکی\n"
        "┐─ 🏭 *کارخانه* — مدیریت کارخانه\n"
        "┐─ 🛍 *بازار* — قیمت‌های بازار\n"
        "┐─ 🏰 *شهر* — وضعیت شهر گروه\n"
        "┐─ 🎲 *بازی* — منوی بازی‌ها\n"
        "┐─ 💳 *انتقال [عدد] @یوزر* — انتقال پوینت\n"
        "┐─ 🐾 *هاپوهام* — پروفایل خودت\n"
        "└─ 🐾 *هاپ هاش* — پروفایل نفر ریپلای‌شده\n\n"
        "پروفایل — پروفایل\nبرترین — لیدربورد"
    )
    await update.message.reply_text(text, parse_mode="Markdown")

# ==================== پنل ادمین مخفی ====================

async def panel_admin_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    if not is_admin(user.id):
        # هیچ پاسخی نمی‌دهیم تا وجود پنل مخفی بماند
        return
    await admin_panel(update, context)

# ==================== هندلرهای خصوصی ====================

async def private_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    text = update.message.text.strip() if update.message.text else ""

    if text == "🐾 هاپوهام":
        await hapoha_profile(update, context)
        return
    
    if text == "📖 راهنما":
        await help_cmd(update, context)
        return
    
    if text == "📊 لیدربورد":
        await top_cmd(update, context)
        return
    
    if text == "🎁 دعوت دوستان":
        await referral_invite_cmd(update, context)
        return
    
    if text == "🛒 مارکت":
        await market_cmd(update, context)
        return

    await update.message.reply_text(
        "🐾 دستورات اصلی فقط تو گروه کار می‌کنن!\nاز دکمه‌های زیر استفاده کن 👇"
    )

# ==================== سیستم ریفرال ====================

def get_referral_count(user_id: int) -> int:
    with db_conn() as conn:
        row = conn.execute(
            "SELECT COUNT(*) FROM referrals WHERE inviter_id=? AND rewarded=1", (user_id,)
        ).fetchone()
        return row[0] if row else 0

async def referral_invite_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    if not REFERRAL_SETTINGS["enabled"]:
        await update.message.reply_text("❌ سیستم دعوت دوستان فعلاً غیرفعاله.")
        return
    
    invite_link = f"https://t.me/{BOT_USERNAME}?start=ref_{user.id}"
    count = get_referral_count(user.id)
    
    text = (
        f"🎁 *دعوت دوستان*\n\n"
        f"لینک اختصاصی تو:\n`{invite_link}`\n\n"
        f"👥 تعداد دعوت‌های موفق: *{count} نفر*\n\n"
        f"💰 جوایز:\n"
        f"┐─ دعوت‌کننده (تو): *{REFERRAL_SETTINGS['reward_sender']:,} هاپ پوینت*\n"
        f"└─ دعوت‌شونده (دوستت): *{REFERRAL_SETTINGS['reward_joiner']:,} هاپ پوینت*\n\n"
        f"⚡️ جایزه بعد از اولین هاپ دوستت واریز می‌شه!"
    )
    kb = InlineKeyboardMarkup([
        [cbtn("📤 اشتراک‌گذاری لینک", url=f"https://t.me/share/url?url={invite_link}&text=بیا%20هاپ%20داگ%20بازی%20کن!")],
        [cbtn("👥 تعداد دعوت‌های من", callback_data="ref_mystats")],
    ])
    await update.message.reply_text(text, parse_mode="Markdown", reply_markup=kb)

# ==================== سیستم مارکت ====================

def market_id_gen():
    return uuid.uuid4().hex[:10]

def get_active_listings():
    with db_conn() as conn:
        return conn.execute(
            "SELECT * FROM user_market WHERE status='active' ORDER BY created_at DESC"
        ).fetchall()

async def market_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    if update.effective_chat.type == "private":
        await update.message.reply_text("🛍 *مارکت کاربران*\n\nبرای خرید از مارکت، به گروه برو و دستور *مارکت* رو بزن.", parse_mode="Markdown")
        return
    
    listings = get_active_listings()
    if not listings:
        await update.message.reply_text("🛒 مارکت خالی‌! هنوز آگهی فعالی ثبت نشده.")
        return
    
    text = "🛒 *مارکت هاپ‌داگ*\n\n"
    kb_rows = []
    for l in listings:
        remaining = l["max_buyers"] - l["buyer_count"]
        text += (
            f"🏷 *{l['title']}*\n"
            f"👤 فروشنده: {l['seller_name']}\n"
            f"💰 قیمت: {l['price']:,} هاپ پوینت\n"
            f"📦 ظرفیت باقی‌مانده: {remaining}\n"
            f"─────────────────\n"
        )
        kb_rows.append([cbtn(f"🛍 خرید «{l['title']}»", callback_data=f"mkt_buy_{l['listing_id']}")])
    await update.message.reply_text(text, parse_mode="Markdown", reply_markup=InlineKeyboardMarkup(kb_rows))

# ==================== هندلرهای کالبک ====================

async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    data = query.data
    user = query.from_user
    
    await query.answer()
    
    # دکمه‌های پنل ادمین
    if data == "adm_stats_users":
        await admin_stats_users(update, context)
    elif data == "adm_stats_groups":
        await admin_stats_groups(update, context)
    elif data == "adm_full_report":
        await admin_full_report(update, context)
    elif data == "adm_mute_menu":
        await admin_mute_menu(update, context)
    elif data == "adm_mute_user":
        await admin_mute_user(update, context)
    elif data == "adm_unmute_user":
        await admin_unmute_user(update, context)
    elif data == "adm_muted_list":
        await admin_muted_list(update, context)
    elif data == "adm_admins_menu":
        await query.edit_message_text("🔑 *مدیریت ادمین‌ها*\n\nبرای افزودن ادمین جدید، روی پیام کاربر ریپلای کن و بنویس `افزودن ادمین`", parse_mode="Markdown")
    elif data == "adm_level_settings":
        await admin_level_settings(update, context)
    elif data == "adm_join_settings":
        await admin_join_settings(update, context)
    elif data == "adm_ref_menu":
        await admin_ref_menu(update, context)
    elif data == "adm_reset_menu":
        await admin_reset_menu(update, context)
    elif data == "adm_antispam_settings":
        await admin_antispam_settings(update, context)
    elif data == "adm_antispam_threshold":
        await admin_antispam_threshold(update, context)
    elif data == "adm_antispam_window":
        await admin_antispam_window(update, context)
    elif data == "adm_antispam_duration":
        await admin_antispam_duration(update, context)
    elif data == "adm_back":
        await admin_panel(update, context)
    
    # جوین اجباری
    elif data == "check_join":
        await check_join_callback(update, context)
    
    # ریفرال
    elif data == "ref_mystats":
        count = get_referral_count(user.id)
        await query.edit_message_text(
            f"👥 *دعوت‌های موفق تو: {count} نفر*\n\n"
            f"💰 جمع جایزه دریافتی: {count * REFERRAL_SETTINGS['reward_sender']:,} هاپ پوینت\n\n"
            f"هر دوستی که با لینک تو بیاد و اولین هاپش رو بزنه = +{REFERRAL_SETTINGS['reward_sender']:,} برای تو!",
            parse_mode="Markdown",
            reply_markup=InlineKeyboardMarkup([[cbtn("🔙 بازگشت", "ref_back")]])
        )
    elif data == "ref_back":
        await referral_invite_cmd(update, context)
    
    # مارکت
    elif data.startswith("mkt_buy_"):
        await query.edit_message_text("🛍 *تأیید خرید*\n\nآیا مطمئنی؟", reply_markup=InlineKeyboardMarkup([
            [cbtn("✅ بله، خریدم!", "mkt_confirm"),
             cbtn("❌ انصراف", "cancel")]
        ]))
    
    elif data == "cancel":
        await query.edit_message_text("❌ عملیات لغو شد.")
    
    else:
        await query.edit_message_text("❌ گزینه نامعتبر!")

# ==================== هندلر گروهی ====================

async def handle_group_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if await force_join_check(update, context):
        return
    
    if not update.message or not update.message.text:
        return
    
    text = update.message.text.strip()
    user = update.effective_user
    
    # ضد اسپم برای همه پیام‌ها در گروه
    is_spam, duration = check_spam(user.id, update.effective_chat.id)
    if is_spam:
        await update.message.reply_text(
            f"🔇 *{user.first_name} به دلیل اسپم {duration//60} دقیقه میو شد!*",
            parse_mode="Markdown"
        )
        return
    
    # هندلر میو (فقط برای ادمین)
    if is_admin(user.id):
        if text.startswith("/mute") and update.message.reply_to_message:
            await handle_mute_command(update, context)
            return
        if text == "/unmute" and update.message.reply_to_message:
            await handle_unmute_command(update, context)
            return
        if text == "/cancel":
            await handle_cancel(update, context)
            return
    
    # دستورات اصلی
    if text in ["هاپ", "هاپو", "hop"]:
        await handle_hop(update, context)
    elif text in ["پروفایل", "profile"]:
        await profile_cmd(update, context)
    elif text in ["برترین", "top"]:
        await top_cmd(update, context)
    elif text in ["راهنما", "help"]:
        await help_cmd(update, context)

async def handle_cancel(update, context):
    user = update.effective_user
    if not is_admin(user.id):
        return
    
    context.user_data.pop("admin_action", None)
    context.user_data.pop("admin_setting", None)
    await update.message.reply_text("❌ عملیات لغو شد.")

# ==================== اصلی ====================

def main():
    init_db()
    
    app = Application.builder().token(BOT_TOKEN).build()
    
    # هندلرهای دستورات
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("profile", profile_cmd))
    app.add_handler(CommandHandler("top", top_cmd))
    app.add_handler(CommandHandler("help", help_cmd))
    app.add_handler(CommandHandler("invite", referral_invite_cmd))
    app.add_handler(CommandHandler("ref", referral_invite_cmd))
    app.add_handler(CommandHandler("mute", handle_mute_command))
    app.add_handler(CommandHandler("unmute", handle_unmute_command))
    app.add_handler(CommandHandler("cancel", handle_cancel))
    app.add_handler(CommandHandler("panel_admin", panel_admin_cmd))  # دستور مخفی پنل ادمین
    
    # هندلرهای پیام
    app.add_handler(MessageHandler(
        filters.ChatType.GROUPS & filters.TEXT & ~filters.COMMAND,
        handle_group_text
    ))
    app.add_handler(MessageHandler(
        filters.ChatType.PRIVATE & ~filters.COMMAND,
        private_message
    ))
    
    # هندلر کالبک
    app.add_handler(CallbackQueryHandler(callback_handler))
    
    logger.info("🐕 ربات هاپی شروع به کار کرد!")
    app.run_polling()

if __name__ == "__main__":
    main()