import asyncio
import json
import re
import string
from collections import defaultdict, Counter
from datetime import datetime, timedelta

import requests
from telethon.sync import TelegramClient
from telethon import events
from telethon.tl.functions.channels import EditBannedRequest, GetParticipantsRequest, EditAdminRequest
from telethon.tl.functions.messages import GetHistoryRequest
from telethon.tl.types import (
    ChatBannedRights,
    ChatAdminRights,
    ChannelParticipantsSearch,
    ChannelParticipantsAdmins,
    InputMessagesFilterPinned,
    UserStatusOnline,
    UserStatusOffline,
    UserStatusRecently,
    UserStatusLastWeek,
    UserStatusLastMonth,
)
from telethon.errors import UserNotParticipantError

import sys
import importlib

if len(sys.argv) < 2:
    print("❌ باید اسم فایل تنظیمات رو بدی. مثال: python bot_core.py acc1")
    sys.exit(1)

config_name = sys.argv[1]
try:
    config = importlib.import_module(config_name)
except ModuleNotFoundError:
    print(f"❌ فایل تنظیمات '{config_name}.py' پیدا نشد.")
    sys.exit(1)

api_id = config.api_id
api_hash = config.api_hash
phone = config.phone
owner_id = config.owner_id
session_name = getattr(config, 'session_name', config_name + '_session')

client = TelegramClient(session_name, api_id, api_hash)

tag_state = {}

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
}

LOCAL_OFFSET = timedelta(hours=3, minutes=30)

ADMIN_FILE = "admins.json"
LOCK_FILE = "locks.json"


def load_admins():
    try:
        with open(ADMIN_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
            return set(data.get("admins", []))
    except Exception:
        return set()


def save_admins():
    try:
        with open(ADMIN_FILE, "w", encoding="utf-8") as f:
            json.dump({"admins": list(admin_ids)}, f)
    except Exception:
        pass


def load_locks():
    try:
        with open(LOCK_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return {}


def save_locks():
    try:
        with open(LOCK_FILE, "w", encoding="utf-8") as f:
            json.dump(lock_state, f)
    except Exception:
        pass


admin_ids = load_admins()
lock_state = load_locks()

LOCK_DEFS = [
    ("link", "🔗 قفل لینک"),
    ("username", "🔖 قفل یوزرنیم"),
    ("sticker", "🎭 قفل استیکر"),
    ("photo", "🖼 قفل عکس"),
    ("video", "🎬 قفل فیلم"),
    ("full", "⛔️ قفل کامل گروه"),
]


def get_chat_locks(chat_id):
    key = str(chat_id)
    if key not in lock_state:
        lock_state[key] = {k: False for k, _ in LOCK_DEFS}
    return lock_state[key]


def render_mode_text(chat_id):
    locks = get_chat_locks(chat_id)
    lines = ["🔐 ━━━━━━━━━━━━━━━━━━━━━ 🔐", "     <b>پنل قفل‌های گروه</b>", "🔐 ━━━━━━━━━━━━━━━━━━━━━ 🔐", ""]
    for i, (key, label) in enumerate(LOCK_DEFS, start=1):
        status = "🔒 فعال" if locks[key] else "🔓 غیرفعال"
        lines.append(f"{i}. {label} — {status}")
    lines.append("")
    lines.append("برای روشن/خاموش کردن، عدد مربوطه رو بفرست، مثال:")
    lines.append("<code>/mode 1</code>")
    return "\n".join(lines)


def authorized(event):
    if event.out:
        return True
    sender_id = event.sender_id
    return sender_id is not None and (sender_id == owner_id or sender_id in admin_ids)


def owner_only(event):
    return event.out or event.sender_id == owner_id


async def respond(event, text, parse_mode=None):
    try:
        if event.out:
            return await event.edit(text, parse_mode=parse_mode)
        else:
            return await event.reply(text, parse_mode=parse_mode)
    except Exception:
        try:
            return await client.send_message(event.chat_id, text, parse_mode=parse_mode)
        except Exception:
            return None


async def start_response(event, text, parse_mode=None):
    if event.out:
        m = await event.edit(text, parse_mode=parse_mode)
        return m if m else event
    else:
        return await event.reply(text, parse_mode=parse_mode)


BLANK = "\u3000"

FONT_5x5 = {
    "A": [".###.", "#...#", "#####", "#...#", "#...#"],
    "B": ["####.", "#...#", "####.", "#...#", "####."],
    "C": [".####", "#....", "#....", "#....", ".####"],
    "D": ["###..", "#..#.", "#..#.", "#..#.", "###.."],
    "E": ["#####", "#....", "###..", "#....", "#####"],
    "F": ["#####", "#....", "###..", "#....", "#...."],
    "G": [".####", "#....", "#.###", "#...#", ".####"],
    "H": ["#...#", "#...#", "#####", "#...#", "#...#"],
    "I": ["#####", "..#..", "..#..", "..#..", "#####"],
    "J": ["..###", "...#.", "...#.", "#..#.", ".##.."],
    "K": ["#...#", "#..#.", "###..", "#..#.", "#...#"],
    "L": ["#....", "#....", "#....", "#....", "#####"],
    "M": ["#...#", "##.##", "#.#.#", "#...#", "#...#"],
    "N": ["#...#", "##..#", "#.#.#", "#..##", "#...#"],
    "O": [".###.", "#...#", "#...#", "#...#", ".###."],
    "P": ["####.", "#...#", "####.", "#....", "#...."],
    "Q": [".###.", "#...#", "#.#.#", "#..#.", ".##.#"],
    "R": ["####.", "#...#", "####.", "#..#.", "#...#"],
    "S": [".####", "#....", ".###.", "....#", "####."],
    "T": ["#####", "..#..", "..#..", "..#..", "..#.."],
    "U": ["#...#", "#...#", "#...#", "#...#", ".###."],
    "V": ["#...#", "#...#", "#...#", ".#.#.", "..#.."],
    "W": ["#...#", "#...#", "#.#.#", "##.##", "#...#"],
    "X": ["#...#", ".#.#.", "..#..", ".#.#.", "#...#"],
    "Y": ["#...#", ".#.#.", "..#..", "..#..", "..#.."],
    "Z": ["#####", "...#.", "..#..", ".#...", "#####"],
    "0": [".###.", "#...#", "#...#", "#...#", ".###."],
    "1": ["..#..", ".##..", "..#..", "..#..", "#####"],
    "2": [".###.", "#...#", "...#.", "..#..", "#####"],
    "3": ["####.", "....#", "..##.", "....#", "####."],
    "4": ["#..#.", "#..#.", "#####", "...#.", "...#."],
    "5": ["#####", "#....", "####.", "....#", "####."],
    "6": [".###.", "#....", "####.", "#...#", ".###."],
    "7": ["#####", "....#", "...#.", "..#..", "..#.."],
    "8": [".###.", "#...#", ".###.", "#...#", ".###."],
    "9": [".###.", "#...#", ".####", "....#", ".###."],
    " ": [".....", ".....", ".....", ".....", "....."],
}

ALLOWED_CHARS = set(string.ascii_letters + string.digits + " ")
MAX_PHRASE_LEN = 15


def build_emoji_art_vertical(phrase, emoji):
    phrase = phrase.upper()
    lines = []
    letters = [ch for ch in phrase if ch != " "] or [" "]

    for idx, ch in enumerate(letters):
        pattern = FONT_5x5[ch]
        for row in pattern:
            line = "".join(emoji if p == "#" else BLANK for p in row)
            lines.append(line)
        if idx != len(letters) - 1:
            lines.append(BLANK * 5)

    return lines


async def delete_after_delay(chat_id, message_ids, delay):
    await asyncio.sleep(delay)
    try:
        await client.delete_messages(chat_id, message_ids)
    except Exception:
        pass


async def resolve_target(event, args_text):
    reply = await event.get_reply_message()
    if reply:
        return await client.get_entity(reply.sender_id)

    if args_text:
        target = args_text.strip().lstrip('@')
        try:
            if target.lstrip('-').isdigit():
                return await client.get_entity(int(target))
            return await client.get_entity(target)
        except Exception:
            return None
    return None


def estimate_join_year(user_id):
    ranges = [
        (10000, 2013), (100000000, 2014), (200000000, 2015),
        (300000000, 2016), (400000000, 2017), (600000000, 2018),
        (900000000, 2019), (1200000000, 2020), (1500000000, 2021),
        (1900000000, 2022), (2100000000, 2023), (2300000000, 2024),
        (2500000000, 2025), (2700000000, 2026),
    ]
    for limit, year in ranges:
        if user_id < limit:
            return year
    return 2026


def status_text(status):
    if isinstance(status, UserStatusOnline):
        return "🟢 آنلاین"
    if isinstance(status, UserStatusOffline):
        return f"⚪️ آفلاین (آخرین بازدید: {status.was_online.strftime('%Y-%m-%d %H:%M')})"
    if isinstance(status, UserStatusRecently):
        return "🟡 اخیراً آنلاین بوده"
    if isinstance(status, UserStatusLastWeek):
        return "🟠 هفته گذشته آنلاین بوده"
    if isinstance(status, UserStatusLastMonth):
        return "🔴 ماه گذشته آنلاین بوده"
    return "نامشخص"


@client.on(events.NewMessage(incoming=True))
async def lock_enforcer(event):
    try:
        chat_id = event.chat_id
        key = str(chat_id)
        if key not in lock_state:
            return
        locks = lock_state[key]
        if not any(locks.values()):
            return

        sender_id = event.sender_id
        if sender_id == owner_id or sender_id in admin_ids:
            return

        if locks.get("full"):
            await event.delete()
            return

        if locks.get("link"):
            text = event.raw_text or ""
            has_link = bool(re.search(r'(https?://|t\.me/|www\.)', text, re.I))
            if not has_link and event.message.entities:
                for ent in event.message.entities:
                    if ent.__class__.__name__ in ("MessageEntityUrl", "MessageEntityTextUrl"):
                        has_link = True
                        break
            if has_link:
                await event.delete()
                return

        if locks.get("username"):
            text = event.raw_text or ""
            if re.search(r'@\w{4,}', text):
                await event.delete()
                return

        if locks.get("sticker") and event.message.sticker:
            await event.delete()
            return

        if locks.get("photo") and event.message.photo:
            await event.delete()
            return

        if locks.get("video") and event.message.video:
            await event.delete()
            return
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/help', func=authorized))
async def help_cmd(event):
    text = """
🌟 ━━━━━━━━━━━━━━━━━━━━━━━ 🌟
       📜 <b>لیست دستورات ربات</b> 📜
🌟 ━━━━━━━━━━━━━━━━━━━━━━━ 🌟

🪪 <b>/p</b> (با ریپلای)
مشخصات کامل شخص.

🚫 <b>/b</b> (ریپلای یا @یوزرنیم یا آیدی)
حذف شخص از گروه (بن).

✅ <b>/unb</b> (ریپلای یا @یوزرنیم یا آیدی)
خارج کردن شخص از لیست بن.

🌤 <b>/v [نام شهر]</b>
وضعیت آب‌وهوا.

🔇 <b>/nos</b> (ریپلای یا آیدی/یوزرنیم)
بی‌صدا کردن شخص.

🔊 <b>/yes</b> (ریپلای یا آیدی/یوزرنیم)
باصدا کردن مجدد.

🏷 <b>/tg [ثانیه] [متن]</b>
تگ کردن اعضا.

⏹ <b>/stop</b>
توقف تگ کردن.

🗑 <b>/del [عدد]</b>
حذف پیام‌های اخیر.

🧹 <b>/dalall</b>
پاکسازی کامل گروه.

💃 <b>/dance</b>
افکت رقص.

🌈 <b>/glow</b>
افکت رنگی.

📌 <b>/pinn</b> (با ریپلای)
پین کردن پیام.

📤 <b>/unpinn</b>
حذف آخرین پین.

👑 <b>/addm</b> (ریپلای یا آیدی/یوزرنیم)
مدیر گروه تلگرام کردن.

⬇️ <b>/naddm</b> (ریپلای یا آیدی/یوزرنیم)
عزل از مدیریت گروه تلگرام.

🔎 <b>/c [سوال یا کلمه]</b>
جستجو و خلاصه از چند منبع مختلف.

✨ <b>/x [ایموجی] [متن انگلیسی/اعداد]</b>
نوشتن عمودی متن با چیدمان خود ایموجی.

🔮 <b>/f</b>
فال حافظ با تعبیر.

➕ <b>/add</b> (ریپلای یا آیدی) [فقط مالک]
اضافه کردن مدیر ربات.

➖ <b>/nadd</b> (ریپلای یا آیدی) [فقط مالک]
عزل مدیر ربات.

🧹 <b>/nnadd</b> [فقط مالک]
پاکسازی تمامی مدیران ربات.

📊 <b>/et</b>
اطلاعات کامل گروه و آمار فعالیت.

🔐 <b>/mode</b> یا <b>/mode [عدد]</b>
نمایش و تغییر قفل‌های گروه.

🏆 <b>/sc</b>
شناسایی دوستان برتر گروه. با ریپلای، درصد رفاقت باهاش رو نشون میده.

🧬 <b>/dna</b> (بدون ریپلای یا با ریپلای)
کارت رفتاری و شخصیتی کامل یک عضو.

🔫 <b>/sh</b> (با ریپلای)
نمایش طنز دوئل بین تو و شخص مورد نظر.

🌟 ━━━━━━━━━━━━━━━━━━━━━━━ 🌟
"""
    await respond(event, text, parse_mode='html')


@client.on(events.NewMessage(pattern='/p$', func=authorized))
async def profile_cmd(event):
    reply = await event.get_reply_message()
    if not reply:
        await respond(event, "⚠️ باید روی پیام شخص مورد نظر ریپلای بزنی.")
        return

    user = await client.get_entity(reply.sender_id)
    chat = await event.get_chat()

    name = f"{user.first_name or ''} {user.last_name or ''}".strip()
    username = f"@{user.username}" if user.username else "ندارد"
    join_year = estimate_join_year(user.id)
    status = status_text(user.status) if user.status else "نامشخص"

    try:
        msgs = await client.get_messages(chat, from_user=user.id, limit=0)
        msg_count = msgs.total
    except Exception:
        msg_count = "نامشخص"

    membership = "✅ عضو گروه"
    try:
        await client.get_permissions(chat, user)
    except UserNotParticipantError:
        membership = "❌ در گروه نیست یا بن شده"
    except Exception:
        membership = "نامشخص"

    text = f"""
🪪 ━━━━━━━━━━━━━━━━━━━━━ 🪪
        <b>پروفایل کاربر</b>
🪪 ━━━━━━━━━━━━━━━━━━━━━ 🪪

👤 <b>نام:</b> {name}
🔖 <b>یوزرنیم:</b> {username}
🆔 <b>آیدی عددی:</b> <code>{user.id}</code>
📅 <b>سال تقریبی ساخت اکانت:</b> {join_year}
📊 <b>وضعیت:</b> {status}
💬 <b>تعداد پیام در این گروه:</b> {msg_count}
👥 <b>وضعیت عضویت:</b> {membership}

🪪 ━━━━━━━━━━━━━━━━━━━━━ 🪪
"""
    await respond(event, text, parse_mode='html')


@client.on(events.NewMessage(pattern=r'/b(?:\s+(.+))?$', func=authorized))
async def ban_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatBannedRights(until_date=None, view_messages=True)
        await client(EditBannedRequest(chat, target, rights))
        name = target.first_name or target.username or str(target.id)
        text = f"🚫 <b>{name}</b> از گروه حذف شد.\nقدرت، بدون توضیح اضافه ⚔️"
        await respond(event, text, parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/unb(?:\s+(.+))?$', func=authorized))
async def unban_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatBannedRights(until_date=None, view_messages=False)
        await client(EditBannedRequest(chat, target, rights))
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"✅ <b>{name}</b> از لیست بن خارج شد، حالا می‌تونه با لینک بیاد.", parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/nos(?:\s+(.+))?$', func=authorized))
async def mute_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatBannedRights(until_date=None, send_messages=True)
        await client(EditBannedRequest(chat, target, rights))
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"🔇 <b>{name}</b> بی‌صدا شد.", parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/yes(?:\s+(.+))?$', func=authorized))
async def unmute_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatBannedRights(until_date=None, send_messages=False)
        await client(EditBannedRequest(chat, target, rights))
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"🔊 <b>{name}</b> باصدا شد.", parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/v(?:\s+(.+))?$', func=authorized))
async def weather_cmd(event):
    city = event.pattern_match.group(1)
    if not city:
        await respond(event, "⚠️ اسم شهر رو هم بنویس. مثال: /v تهران")
        return

    try:
        url = f"https://wttr.in/{city}?format=j1&lang=fa"
        resp = requests.get(url, headers=HEADERS, timeout=10)
        data = resp.json()

        current = data['current_condition'][0]
        today = data['weather'][0]
        tomorrow = data['weather'][1]

        now_time = datetime.now().strftime('%H:%M')

        text = f"""
🌤 ━━━━━━━━━━━━━━━━━━━━━ 🌤
     <b>آب و هوای {city}</b>
🌤 ━━━━━━━━━━━━━━━━━━━━━ 🌤

🕒 <b>ساعت فعلی (تقریبی):</b> {now_time}
🌡 <b>دمای فعلی:</b> {current['temp_C']}°C (حس‌شده {current['FeelsLikeC']}°C)
💧 <b>رطوبت:</b> {current['humidity']}%

📅 <b>امروز:</b> {today['mintempC']}°C تا {today['maxtempC']}°C
📅 <b>فردا:</b> {tomorrow['mintempC']}°C تا {tomorrow['maxtempC']}°C

🌤 ━━━━━━━━━━━━━━━━━━━━━ 🌤
"""
        await respond(event, text, parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ نتونستم اطلاعات آب‌وهوا رو بگیرم: {e}")


@client.on(events.NewMessage(pattern=r'/tg(?:\s+(\d+))?(?:\s+(.+))?$', func=authorized))
async def tag_cmd(event):
    chat = await event.get_chat()
    chat_id = event.chat_id
    interval = event.pattern_match.group(1)
    extra_text = event.pattern_match.group(2)
    interval = int(interval) if interval else 3

    try:
        await event.delete()
    except Exception:
        pass

    participants = await client.get_participants(chat)
    participants = [p for p in participants if not p.bot]
    participants.sort(key=lambda p: 0 if isinstance(p.status, UserStatusOnline) else 1)

    tag_state[chat_id] = {"running": True, "sent_ids": []}

    for user in participants:
        if not tag_state.get(chat_id, {}).get("running"):
            break

        display_name = user.first_name or user.username or "کاربر"
        mention = f'<a href="tg://user?id={user.id}">{display_name}</a>'
        text = mention
        if extra_text:
            text += f" {extra_text}"
        else:
            text += " توجه کن ⬆️"

        try:
            msg = await client.send_message(chat_id, text, parse_mode='html')
            tag_state[chat_id]["sent_ids"].append(msg.id)
        except Exception:
            pass

        await asyncio.sleep(interval)

    if chat_id in tag_state:
        ids = tag_state[chat_id]["sent_ids"]
        asyncio.create_task(delete_after_delay(chat_id, ids, 10))
        tag_state[chat_id]["running"] = False


@client.on(events.NewMessage(pattern='/stop', func=authorized))
async def stop_tag_cmd(event):
    chat_id = event.chat_id
    if chat_id in tag_state:
        tag_state[chat_id]["running"] = False
    await respond(event, "⏹ عملیات تگ متوقف شد. تگ‌ها تا ۱۰ ثانیه دیگه پاک میشن.")


@client.on(events.NewMessage(pattern=r'/del\s+(\d+)$', func=authorized))
async def del_cmd(event):
    count = int(event.pattern_match.group(1))
    count = min(count, 10000)
    chat = await event.get_chat()

    try:
        await event.delete()
    except Exception:
        pass

    deleted = 0
    async for msg in client.iter_messages(chat, limit=count):
        try:
            await msg.delete()
            deleted += 1
        except Exception:
            pass

    confirm = await client.send_message(chat, f"🧹 <b>{deleted}</b> پیام با موفقیت پاک شد. نظم برقرار شد 💪", parse_mode='html')
    await asyncio.sleep(5)
    try:
        await confirm.delete()
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/dalall', func=authorized))
async def del_all_cmd(event):
    chat = await event.get_chat()
    await respond(event, "🧹 عملیات پاکسازی کامل گروه شروع شد، لطفاً صبر کن...")

    deleted_total = 0
    while True:
        batch_deleted = 0
        async for msg in client.iter_messages(chat, limit=100):
            try:
                await msg.delete()
                batch_deleted += 1
                deleted_total += 1
            except Exception:
                pass
        if batch_deleted == 0:
            break

    try:
        await client.send_message(chat, f"✅ پاکسازی کامل شد. مجموعاً {deleted_total} پیام حذف شد.")
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/dance', func=authorized))
async def dance_cmd(event):
    frames = ["🕺", "💃", "🕺‍♂️", "🪩🕺🪩", "✨💃✨", "🕺💥", "💃🌟", "🪩✨🪩"]
    msg = await start_response(event, frames[0])
    for i in range(20):
        await asyncio.sleep(0.4)
        try:
            await msg.edit(frames[i % len(frames)])
        except Exception:
            pass
    try:
        await msg.edit("💃🕺 رقص تموم شد! 🕺💃")
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/glow', func=authorized))
async def glow_cmd(event):
    colors = ["🟥🟧🟨🟩🟦🟪", "🟪🟥🟧🟨🟩🟦", "🟦🟪🟥🟧🟨🟩",
              "🟩🟦🟪🟥🟧🟨", "🟨🟩🟦🟪🟥🟧", "🟧🟨🟩🟦🟪🟥"]
    msg = await start_response(event, "✨ در حال آماده‌سازی ✨")
    for i in range(25):
        await asyncio.sleep(0.4)
        try:
            await msg.edit(f"{colors[i % len(colors)]}\n✨ ✨ ✨\n{colors[(i+3) % len(colors)]}")
        except Exception:
            pass
    try:
        await msg.edit("🌈✨ نمایش تموم شد ✨🌈")
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/pinn$', func=authorized))
async def pin_cmd(event):
    reply = await event.get_reply_message()
    if not reply:
        await respond(event, "⚠️ باید روی پیام مورد نظر ریپلای بزنی.")
        return

    try:
        await client.pin_message(event.chat_id, reply, notify=True)
        await respond(event, "📌 پیام با موفقیت پین شد ✨")
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern='/unpinn$', func=authorized))
async def unpin_cmd(event):
    chat = await event.get_chat()
    try:
        pinned = await client.get_messages(chat, filter=InputMessagesFilterPinned, limit=1)
        if not pinned:
            await respond(event, "⚠️ هیچ پیام پین‌شده‌ای وجود نداره.")
            return
        await client.unpin_message(chat, pinned[0].id)
        await respond(event, "📤 آخرین پیام پین‌شده حذف شد.")
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/addm(?:\s+(.+))?$', func=authorized))
async def addm_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatAdminRights(
            change_info=True,
            post_messages=True,
            edit_messages=True,
            delete_messages=True,
            ban_users=True,
            invite_users=True,
            pin_messages=True,
            add_admins=False,
            anonymous=False,
            manage_call=True,
            other=True,
        )
        await client(EditAdminRequest(chat, target, rights, rank="مدیر"))
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"👑 <b>{name}</b> به عنوان مدیر گروه منصوب شد. مبارکه! 🎉", parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


@client.on(events.NewMessage(pattern=r'/naddm(?:\s+(.+))?$', func=authorized))
async def naddm_cmd(event):
    args = event.pattern_match.group(1)
    chat = await event.get_chat()
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    try:
        rights = ChatAdminRights(
            change_info=False,
            post_messages=False,
            edit_messages=False,
            delete_messages=False,
            ban_users=False,
            invite_users=False,
            pin_messages=False,
            add_admins=False,
            anonymous=False,
            manage_call=False,
            other=False,
        )
        await client(EditAdminRequest(chat, target, rights, rank=""))
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"⬇️ <b>{name}</b> از مدیریت گروه عزل شد.", parse_mode='html')
    except Exception as e:
        await respond(event, f"❌ خطا: {e}")


async def get_wiki_extract(domain, title):
    url = f"https://{domain}/w/api.php"
    params = {
        "action": "query", "prop": "extracts", "exintro": True,
        "explaintext": True, "titles": title, "format": "json",
        "utf8": 1, "redirects": 1,
    }
    resp = requests.get(url, params=params, headers=HEADERS, timeout=10).json()
    pages = resp.get("query", {}).get("pages", {})
    page = next(iter(pages.values()), {})
    extract = page.get("extract", "").strip()
    if extract and len(extract) > 900:
        extract = extract[:900].rsplit(' ', 1)[0] + "..."
    return extract


def strip_tags(html_text):
    return re.sub('<[^<]+?>', '', html_text).strip()


SEARX_INSTANCES = [
    "https://searx.be",
    "https://priv.au",
    "https://baresearch.org",
    "https://opnxng.com",
]


async def searx_search(query):
    for base in SEARX_INSTANCES:
        try:
            r = requests.get(
                f"{base}/search",
                params={"q": query, "format": "json", "language": "fa"},
                headers=HEADERS, timeout=8,
            )
            data = r.json()
            results = data.get("results", [])
            if results:
                top = results[0]
                title = (top.get("title") or query).strip()
                content = strip_tags(top.get("content") or "")
                if content:
                    return title, content
        except Exception:
            continue
    return None, None


async def search_multi_engine(query):
    try:
        wiki_url = "https://fa.wikipedia.org/w/api.php"
        params = {"action": "query", "list": "search", "srsearch": query,
                  "format": "json", "srlimit": 1, "utf8": 1}
        r = requests.get(wiki_url, params=params, headers=HEADERS, timeout=10).json()
        results = r.get("query", {}).get("search", [])
        if results:
            title = results[0]["title"]
            extract = await get_wiki_extract("fa.wikipedia.org", title)
            if extract:
                return title, extract, "ویکی‌پدیا"
    except Exception:
        pass

    try:
        wiki_url = "https://fa.wikipedia.org/w/api.php"
        params = {"action": "opensearch", "search": query, "limit": 1, "namespace": 0, "format": "json"}
        r = requests.get(wiki_url, params=params, headers=HEADERS, timeout=10).json()
        titles = r[1] if len(r) > 1 else []
        if titles:
            title = titles[0]
            extract = await get_wiki_extract("fa.wikipedia.org", title)
            if extract:
                return title, extract, "ویکی‌پدیا"
    except Exception:
        pass

    try:
        wikt_url = "https://fa.wiktionary.org/w/api.php"
        params = {"action": "opensearch", "search": query, "limit": 1, "namespace": 0, "format": "json"}
        r = requests.get(wikt_url, params=params, headers=HEADERS, timeout=10).json()
        titles = r[1] if len(r) > 1 else []
        if titles:
            title = titles[0]
            extract = await get_wiki_extract("fa.wiktionary.org", title)
            if extract:
                return title, extract, "ویکشنری"
    except Exception:
        pass

    try:
        title, content = await searx_search(query)
        if content:
            if len(content) > 900:
                content = content[:900].rsplit(' ', 1)[0] + "..."
            return title, content, "جستجوی وب (Searx)"
    except Exception:
        pass

    try:
        ddg_resp = requests.get(
            "https://api.duckduckgo.com/",
            params={"q": query, "format": "json", "no_html": 1, "skip_disambig": 1},
            headers=HEADERS, timeout=10,
        ).json()
        abstract = ddg_resp.get("AbstractText", "").strip()
        heading = ddg_resp.get("Heading", "").strip() or query
        if not abstract:
            for item in ddg_resp.get("RelatedTopics", []):
                if isinstance(item, dict) and item.get("Text"):
                    abstract = item["Text"].strip()
                    break
        if abstract:
            if len(abstract) > 900:
                abstract = abstract[:900].rsplit(' ', 1)[0] + "..."
            return heading, abstract, "DuckDuckGo"
    except Exception:
        pass

    try:
        r = requests.get(
            "https://html.duckduckgo.com/html/", params={"q": query},
            headers=HEADERS, timeout=10,
        )
        snippets = re.findall(r'class="result__snippet"[^>]*>(.*?)</a>', r.text, re.S)
        if snippets:
            clean = strip_tags(snippets[0])
            if clean:
                if len(clean) > 900:
                    clean = clean[:900].rsplit(' ', 1)[0] + "..."
                return query, clean, "DuckDuckGo (وب)"
    except Exception:
        pass

    return None, None, None


@client.on(events.NewMessage(pattern=r'/c(?:\s+(.+))?$', func=authorized))
async def search_cmd(event):
    query = event.pattern_match.group(1)
    if not query:
        await respond(event, "⚠️ چیزی برای جستجو بنویس. مثال: /c نانی چیه")
        return

    msg = await start_response(event, f"🔎 در حال جستجوی «{query}»...")

    title, extract, source = await search_multi_engine(query)

    if extract:
        text = f"""
🔎 ━━━━━━━━━━━━━━━━━━━━━ 🔎
     <b>{title}</b>
🔎 ━━━━━━━━━━━━━━━━━━━━━ 🔎

{extract}

📖 منبع: {source}
"""
        try:
            await msg.edit(text, parse_mode='html')
        except Exception:
            pass
    else:
        try:
            await msg.edit(f"❌ متأسفانه چیزی برای «{query}» توی هیچ‌کدوم از منابع پیدا نکردم.")
        except Exception:
            pass


@client.on(events.NewMessage(pattern=r'/x\s+(\S+)\s+(.+)$', func=authorized))
async def fx_cmd(event):
    emoji = event.pattern_match.group(1)
    phrase = event.pattern_match.group(2).strip()

    if not phrase or any(ch not in ALLOWED_CHARS for ch in phrase):
        await respond(event, "⚠️ فقط حروف انگلیسی (A-Z) و اعداد (0-9) پشتیبانی میشه.\nمثال: /x 🥲 GOOD")
        return

    if len(phrase) > MAX_PHRASE_LEN:
        await respond(event, f"⚠️ متن خیلی طولانیه، حداکثر {MAX_PHRASE_LEN} کاراکتر بنویس.")
        return

    total_duration = 9
    intro_frames = 3
    reveal_frames = 12
    interval = total_duration / (intro_frames + reveal_frames)

    msg = await start_response(event, emoji)

    for i in range(1, intro_frames + 1):
        wall = "\n".join([emoji * 5] * i)
        try:
            await msg.edit(f"<pre>{wall}</pre>", parse_mode='html')
        except Exception:
            pass
        await asyncio.sleep(interval)

    lines = build_emoji_art_vertical(phrase, emoji)
    total_rows = len(lines)

    for i in range(1, reveal_frames + 1):
        revealed = max(1, int(total_rows * i / reveal_frames))
        try:
            await msg.edit(f"<pre>{chr(10).join(lines[:revealed])}</pre>", parse_mode='html')
        except Exception:
            pass
        await asyncio.sleep(interval)

    final_art = "\n".join(lines)
    try:
        await msg.edit(f"<pre>{final_art}</pre>", parse_mode='html')
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/f$', func=authorized))
async def fortune_cmd(event):
    msg = await start_response(event, "🔮 در حال گرفتن فال...")
    try:
        r = requests.get("https://hafez-dxle.onrender.com/fal", headers=HEADERS, timeout=10)
        data = r.json()
        title = data.get("title", "فال حافظ")
        poem = data.get("poem") or data.get("text") or data.get("ghazal") or ""
        meaning = data.get("interpreter") or data.get("meaning") or data.get("interpretation") or ""

        text = f"""
🔮 ━━━━━━━━━━━━━━━━━━━━━ 🔮
       <b>فال حافظ</b>
🔮 ━━━━━━━━━━━━━━━━━━━━━ 🔮

<b>{title}</b>

{poem}

📖 <b>تعبیر:</b>
{meaning}

🔮 ━━━━━━━━━━━━━━━━━━━━━ 🔮
"""
        await msg.edit(text, parse_mode='html')
    except Exception as e:
        try:
            await msg.edit(f"❌ فعلاً نتونستم فال بگیرم، دوباره امتحان کن. ({e})")
        except Exception:
            pass


@client.on(events.NewMessage(pattern=r'/add(?:\s+(.+))?$', func=owner_only))
async def add_admin_cmd(event):
    args = event.pattern_match.group(1)
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    if target.id == owner_id:
        await respond(event, "👑 این شخص از قبل مالک رباته.")
        return

    admin_ids.add(target.id)
    save_admins()
    name = target.first_name or target.username or str(target.id)
    await respond(event, f"✅ <b>{name}</b> به مدیران ربات اضافه شد و حالا به تمام دستورات دسترسی داره.", parse_mode='html')


@client.on(events.NewMessage(pattern=r'/nadd(?:\s+(.+))?$', func=owner_only))
async def remove_admin_cmd(event):
    args = event.pattern_match.group(1)
    target = await resolve_target(event, args)

    if not target:
        await respond(event, "⚠️ باید ریپلای بزنی یا آیدی/یوزرنیم بدی.")
        return

    if target.id in admin_ids:
        admin_ids.discard(target.id)
        save_admins()
        name = target.first_name or target.username or str(target.id)
        await respond(event, f"⬇️ <b>{name}</b> از مدیریت ربات عزل شد.", parse_mode='html')
    else:
        await respond(event, "⚠️ این شخص در لیست مدیران ربات نیست.")


@client.on(events.NewMessage(pattern=r'/nnadd$', func=owner_only))
async def clear_admins_cmd(event):
    count = len(admin_ids)
    admin_ids.clear()
    save_admins()
    await respond(event, f"🧹 تمامی {count} مدیر ربات پاکسازی و عزل شدند.")


@client.on(events.NewMessage(pattern='/et$', func=authorized))
async def group_info_cmd(event):
    chat = await event.get_chat()
    msg = await start_response(event, "📊 در حال جمع‌آوری اطلاعات گروه...")

    group_name = getattr(chat, 'title', 'نامشخص')
    group_id = event.chat_id

    creation_text = "نامشخص"
    try:
        first_msgs = await client.get_messages(chat, limit=1, reverse=True)
        if first_msgs:
            creation_text = first_msgs[0].date.strftime('%Y-%m-%d')
    except Exception:
        pass

    admins_text = "نامشخص"
    try:
        participants = await client.get_participants(chat, filter=ChannelParticipantsAdmins)
        lines = []
        for p in participants[:15]:
            pname = p.first_name or p.username or str(p.id)
            lines.append(f"• {pname} (<code>{p.id}</code>)")
        admins_text = "\n".join(lines) if lines else "یافت نشد"
    except Exception:
        pass

    msg_count_hour = 0
    try:
        one_hour_ago = datetime.utcnow() - timedelta(hours=1)
        async for m in client.iter_messages(chat, limit=500):
            if m.date.replace(tzinfo=None) < one_hour_ago:
                break
            msg_count_hour += 1
    except Exception:
        pass
    activity_percent = min(100, round(msg_count_hour / 200 * 100))

    top_text = "نامشخص"
    try:
        tally = {}
        names = {}
        async for m in client.iter_messages(chat, limit=300):
            if m.sender_id:
                tally[m.sender_id] = tally.get(m.sender_id, 0) + 1
                if m.sender_id not in names and m.sender:
                    names[m.sender_id] = getattr(m.sender, 'first_name', None) or getattr(m.sender, 'username', None) or str(m.sender_id)
        top_sorted = sorted(tally.items(), key=lambda x: x[1], reverse=True)[:5]
        lines = []
        for uid, cnt in top_sorted:
            uname = names.get(uid, str(uid))
            lines.append(f"• {uname} (<code>{uid}</code>) — {cnt} پیام")
        top_text = "\n".join(lines) if lines else "داده‌ای یافت نشد"
    except Exception:
        pass

    total_members = "نامشخص"
    try:
        total_members = getattr(chat, 'participants_count', None) or "نامشخص"
    except Exception:
        pass

    text = f"""
📊 ━━━━━━━━━━━━━━━━━━━━━ 📊
       <b>اطلاعات کامل گروه</b>
📊 ━━━━━━━━━━━━━━━━━━━━━ 📊

🏷 <b>نام گروه:</b> {group_name}
🆔 <b>آیدی عددی:</b> <code>{group_id}</code>
📅 <b>تاریخ تقریبی ساخت (اولین پیام):</b> {creation_text}
👥 <b>تعداد اعضا:</b> {total_members}
🔥 <b>میزان فعالیت (۱ ساعت اخیر):</b> {activity_percent}٪ ({msg_count_hour} پیام)

👑 <b>مدیران گروه:</b>
{admins_text}

🏆 <b>نفرات برتر فعال (۳۰۰ پیام اخیر):</b>
{top_text}

📊 ━━━━━━━━━━━━━━━━━━━━━ 📊
"""
    try:
        await msg.edit(text, parse_mode='html')
    except Exception:
        pass


@client.on(events.NewMessage(pattern=r'/mode(?:\s+(\d+))?$', func=authorized))
async def mode_cmd(event):
    chat_id = event.chat_id
    choice = event.pattern_match.group(1)
    locks = get_chat_locks(chat_id)

    if choice:
        idx = int(choice) - 1
        if 0 <= idx < len(LOCK_DEFS):
            key, _ = LOCK_DEFS[idx]
            locks[key] = not locks[key]
            save_locks()

    await respond(event, render_mode_text(chat_id), parse_mode='html')


POSITIVE_WORDS = [
    "ممنون", "مرسی", "قربان", "عزیز", "دمت گرم", "دمتون گرم", "خوبی", "دوست دارم",
    "کمکت", "کمک می", "باحال", "عالی", "افتخار", "قشنگ", "خوشحال", "لطف", "مهربون",
    "رفیق", "داداش", "آبجی", "همراه", "پشتیبان", "حمایت", "بهترین", "فداتشم", "قربانت",
    "خیلی خوب", "احسنت", "دستت درد نکنه", "همیشه هستی", "امیدوارم", "موفق باشی",
]

PLAYFUL_EMOJIS = {"😂", "🤣", "😜", "😝", "🤪", "😅", "🙃", "😹", "😆", "😁"}
CALM_EMOJIS = {"😐", "😑", "🙁", "☹", "😤", "🙄", "😒", "🤨", "😶"}

EMOJI_RANGES = [
    (0x1F600, 0x1F64F), (0x1F300, 0x1F5FF), (0x1F680, 0x1F6FF),
    (0x1F1E0, 0x1F1FF), (0x2700, 0x27BF), (0x1F900, 0x1F9FF),
    (0x2600, 0x26FF), (0x1FA70, 0x1FAFF), (0x2B00, 0x2BFF),
]


def is_emoji_char(ch):
    cp = ord(ch)
    return any(lo <= cp <= hi for lo, hi in EMOJI_RANGES)


async def fetch_messages_by_ids(chat, ids):
    result = {}
    ids = list(set(ids))
    for i in range(0, len(ids), 100):
        batch = ids[i:i + 100]
        try:
            msgs = await client.get_messages(chat, ids=batch)
            for m in msgs:
                if m:
                    result[m.id] = m
        except Exception:
            pass
    return result


async def analyze_pair(chat, target_id, issuer_id, msg_limit=400, days_limit=90):
    since = datetime.utcnow() - timedelta(days=days_limit)

    target_msgs = []
    async for m in client.iter_messages(chat, from_user=target_id, limit=msg_limit):
        if m.date.replace(tzinfo=None) < since:
            break
        target_msgs.append(m)

    issuer_msgs = []
    async for m in client.iter_messages(chat, from_user=issuer_id, limit=msg_limit):
        if m.date.replace(tzinfo=None) < since:
            break
        issuer_msgs.append(m)

    days_active = len(set(m.date.strftime('%Y-%m-%d') for m in target_msgs))
    pos_count = sum(1 for m in target_msgs if any(w in (m.raw_text or "") for w in POSITIVE_WORDS))

    reply_ids = set()
    for m in target_msgs:
        if m.reply_to_msg_id:
            reply_ids.add(m.reply_to_msg_id)
    for m in issuer_msgs:
        if m.reply_to_msg_id:
            reply_ids.add(m.reply_to_msg_id)

    parents = await fetch_messages_by_ids(chat, reply_ids) if reply_ids else {}

    mutual = 0
    target_to_issuer = 0
    issuer_to_target = 0
    response_deltas = []

    for m in target_msgs:
        if m.reply_to_msg_id:
            parent = parents.get(m.reply_to_msg_id)
            if parent and parent.sender_id == issuer_id:
                mutual += 1
                target_to_issuer += 1
                delta = (m.date - parent.date).total_seconds()
                if 0 <= delta <= 3600 * 6:
                    response_deltas.append(delta)

    for m in issuer_msgs:
        if m.reply_to_msg_id:
            parent = parents.get(m.reply_to_msg_id)
            if parent and parent.sender_id == target_id:
                mutual += 1
                issuer_to_target += 1
                delta = (m.date - parent.date).total_seconds()
                if 0 <= delta <= 3600 * 6:
                    response_deltas.append(delta)

    avg_response_min = round(sum(response_deltas) / len(response_deltas) / 60, 1) if response_deltas else None
    quick_pct = round(sum(1 for d in response_deltas if d <= 300) / len(response_deltas) * 100) if response_deltas else None

    reaction_total = 0
    for m in target_msgs:
        try:
            if m.reactions and m.reactions.results:
                for r in m.reactions.results:
                    reaction_total += r.count
        except Exception:
            pass

    balance_note = None
    if target_to_issuer > 0 or issuer_to_target > 0:
        bigger = max(target_to_issuer, issuer_to_target)
        smaller = min(target_to_issuer, issuer_to_target)
        if bigger > 0 and smaller / bigger < 0.4 and bigger >= 3:
            if target_to_issuer > issuer_to_target:
                balance_note = "بیشتر این طرف مقابله که بهت جواب می‌ده تا برعکس."
            else:
                balance_note = "بیشتر این تویی که بهش پیام و جواب می‌دی."
        else:
            balance_note = "گفتگو بین شما نسبتاً دوطرفه و متعادله."

    return {
        "days_active": days_active,
        "pos_count": pos_count,
        "mutual": mutual,
        "target_msg_count": len(target_msgs),
        "avg_response_min": avg_response_min,
        "quick_pct": quick_pct,
        "reaction_total": reaction_total,
        "balance_note": balance_note,
    }


async def analyze_group(chat, limit=800):
    messages = []
    async for m in client.iter_messages(chat, limit=limit):
        messages.append(m)
    messages.reverse()

    msg_sender_map = {}
    user_days = defaultdict(set)
    user_pos_count = defaultdict(int)
    reply_pairs = defaultdict(int)

    for m in messages:
        if not m.sender_id:
            continue
        msg_sender_map[m.id] = m.sender_id
        user_days[m.sender_id].add(m.date.strftime('%Y-%m-%d'))
        text = m.raw_text or ""
        if text:
            for w in POSITIVE_WORDS:
                if w in text:
                    user_pos_count[m.sender_id] += 1
        if m.reply_to_msg_id:
            parent_sender = msg_sender_map.get(m.reply_to_msg_id)
            if parent_sender and parent_sender != m.sender_id:
                reply_pairs[(m.sender_id, parent_sender)] += 1

    return user_days, user_pos_count, reply_pairs


def compute_mutual_scores(reply_pairs):
    mutual_score = defaultdict(int)
    seen = set()
    for (a, b), cnt in reply_pairs.items():
        if (a, b) in seen or (b, a) in seen:
            continue
        other = reply_pairs.get((b, a), 0)
        if other:
            m = min(cnt, other)
            mutual_score[a] += m
            mutual_score[b] += m
        seen.add((a, b))
        seen.add((b, a))
    return mutual_score


@client.on(events.NewMessage(pattern='/sc$', func=authorized))
async def social_score_cmd(event):
    chat = await event.get_chat()
    reply = await event.get_reply_message()

    msg = await start_response(event, "🔍 در حال ارزیابی روابط گروه، لطفاً چند لحظه صبر کن...")
    await asyncio.sleep(4)

    try:
        participants = await client.get_participants(chat)
        present_ids = {p.id: p for p in participants if not p.bot}

        if reply:
            target_id = reply.sender_id
            if target_id not in present_ids:
                await msg.edit("⚠️ این شخص دیگه عضو گروه نیست.")
                return

            issuer_id = event.sender_id if not event.out else owner_id

            stats = await analyze_pair(chat, target_id, issuer_id)
            mutual = stats["mutual"]
            pos_count = stats["pos_count"]
            days = stats["days_active"]
            msg_count = stats["target_msg_count"]
            reaction_total = stats["reaction_total"]
            quick_pct = stats["quick_pct"]

            raw = (
                mutual * 20
                + pos_count * 5
                + min(15, days * 3)
                + min(10, msg_count // 5)
                + min(10, reaction_total)
                + (quick_pct // 10 if quick_pct else 0)
            )
            total_pct = min(100, raw)

            target = present_ids[target_id]
            name = target.first_name or target.username or str(target_id)

            if total_pct >= 60:
                verdict = "🌟 این آدم واقعاً ارزش سرمایه‌گذاری روی دوستیش رو داره."
            elif total_pct >= 25:
                verdict = "🙂 پایه‌ی خوبی برای رفاقت هست، با تعامل بیشتر می‌تونه قوی‌تر بشه."
            else:
                verdict = "🤔 هنوز داده‌ی کافی برای قضاوت دقیق نیست، شاید نیاز به تعامل بیشتر باشه."

            response_line = (
                f"⚡ <b>سرعت پاسخ‌گویی:</b> میانگین {stats['avg_response_min']} دقیقه ({quick_pct}٪ زیر ۵ دقیقه)"
                if stats["avg_response_min"] is not None
                else "⚡ <b>سرعت پاسخ‌گویی:</b> داده‌ی کافی نیست"
            )
            balance_line = stats["balance_note"] or "داده‌ی کافی برای سنجش توازن گفتگو نیست."

            text = f"""
✨ ━━━━━━━━━━━━━━━━━━━━━ ✨
   <b>تحلیل رفاقت با {name}</b>
✨ ━━━━━━━━━━━━━━━━━━━━━ ✨

🔁 <b>ریپلای متقابل با تو:</b> {mutual} بار
💬 <b>پیام‌های مثبت و صمیمی:</b> {pos_count} مورد
📅 <b>روزهای حضور فعال (۹۰ روز اخیر):</b> {days} روز
❤️ <b>ری‌اکشن‌های دریافتی:</b> {reaction_total} مورد
{response_line}
⚖️ <b>توازن گفتگو:</b> {balance_line}

💯 <b>درصد همراهی و رفاقت:</b> {total_pct}٪

{verdict}

✨ ━━━━━━━━━━━━━━━━━━━━━ ✨
"""
            try:
                await msg.edit(text, parse_mode='html')
            except Exception:
                pass
            return

        user_days, user_pos_count, reply_pairs = await analyze_group(chat, limit=800)
        mutual_score = compute_mutual_scores(reply_pairs)

        raw_scores = {}
        for uid in present_ids:
            m = mutual_score.get(uid, 0)
            p = user_pos_count.get(uid, 0)
            d = len(user_days.get(uid, set()))
            raw_scores[uid] = m * 4 + p * 2 + d * 1

        scored = [(uid, s) for uid, s in raw_scores.items() if s > 0]
        scored.sort(key=lambda x: x[1], reverse=True)
        top3 = scored[:3]

        if not top3:
            try:
                await msg.edit("⚠️ فعلاً داده‌ی کافی برای شناسایی دوستان برتر گروه پیدا نشد.")
            except Exception:
                pass
            return

        medals = ["🥇", "🥈", "🥉"]
        lines = [
            "🏆 ━━━━━━━━━━━━━━━━━━━━━ 🏆",
            "     <b>دوستان برتر و باوفای گروه</b>",
            "🏆 ━━━━━━━━━━━━━━━━━━━━━ 🏆",
            "",
        ]
        for i, (uid, score) in enumerate(top3):
            p = present_ids[uid]
            name = p.first_name or p.username or str(uid)
            mutual = mutual_score.get(uid, 0)
            pos = user_pos_count.get(uid, 0)
            days = len(user_days.get(uid, set()))
            lines.append(f"{medals[i]} <b>{name}</b>")
            lines.append(f"با {mutual} تعامل متقابل و {pos} پیام صمیمی، حسابی معلومه که رفیق واقعیه.")
            lines.append(f"حضورش توی {days} روز اخیر هم نشون میده همیشه پای گروه هست. 🤝")
            lines.append("")

        lines.append("🏆 ━━━━━━━━━━━━━━━━━━━━━ 🏆")
        text = "\n".join(lines)

        try:
            await msg.edit(text, parse_mode='html')
        except Exception:
            pass

    except Exception as e:
        try:
            await msg.edit(f"❌ خطا در تحلیل گروه: {e}")
        except Exception:
            pass


async def analyze_dna(chat, user_id, limit=1500, days_limit=120):
    since = datetime.utcnow() - timedelta(days=days_limit)
    messages = []
    async for m in client.iter_messages(chat, from_user=user_id, limit=limit):
        if m.date.replace(tzinfo=None) < since:
            break
        messages.append(m)

    if not messages:
        return None

    emoji_counter = Counter()
    hour_counter = Counter()
    weekday_counter = Counter()
    type_counter = Counter()
    lengths = []
    question = 0
    laugh = 0

    reply_ids = []
    for m in messages:
        local_dt = m.date.replace(tzinfo=None) + LOCAL_OFFSET

        text = m.raw_text or ""
        for ch in text:
            if is_emoji_char(ch):
                emoji_counter[ch] += 1
        hour_counter[local_dt.hour] += 1
        weekday_counter[local_dt.weekday()] += 1

        if m.sticker:
            type_counter["sticker"] += 1
        elif m.photo:
            type_counter["photo"] += 1
        elif m.video:
            type_counter["video"] += 1
        elif m.voice:
            type_counter["voice"] += 1
        elif m.document and not m.sticker:
            type_counter["doc"] += 1
        elif text:
            type_counter["text"] += 1

        if text:
            lengths.append(len(text))
            question += text.count("؟") + text.count("?")
            if "😂" in text or "🤣" in text or "هاها" in text or "خخخ" in text:
                laugh += 1

        if m.reply_to_msg_id:
            reply_ids.append((m.id, m.reply_to_msg_id, m.date))

    parents = await fetch_messages_by_ids(chat, [rid for _, rid, _ in reply_ids]) if reply_ids else {}
    deltas = []
    for mid, rid, mdate in reply_ids:
        parent = parents.get(rid)
        if parent and parent.sender_id != user_id:
            delta_sec = (mdate - parent.date).total_seconds()
            if 0 <= delta_sec <= 3600 * 6:
                deltas.append(delta_sec)

    avg_response_min = round(sum(deltas) / len(deltas) / 60, 1) if deltas else None
    quick_pct = round(sum(1 for d in deltas if d <= 300) / len(deltas) * 100) if deltas else None

    total_msgs = len(messages)
    avg_len = round(sum(lengths) / len(lengths)) if lengths else 0
    top_emojis = emoji_counter.most_common(3)

    playful_score = sum(cnt for e, cnt in emoji_counter.items() if e in PLAYFUL_EMOJIS) + laugh
    calm_score = sum(cnt for e, cnt in emoji_counter.items() if e in CALM_EMOJIS)

    if playful_score >= calm_score * 2 and playful_score >= 5:
        mood = "شوخ‌طبع و پرانرژی 😄"
        mood_reason = f"با استفاده‌ی {playful_score} باره از ایموجی و لحن‌های خنده، مشخصه که همیشه فضا رو شاد نگه می‌داره."
    elif calm_score > playful_score and calm_score >= 3:
        mood = "آروم و متین 🧘"
        mood_reason = f"لحن نوشتاریش بیشتر جدی و آرومه، با {calm_score} بار استفاده از ایموجی‌های خنثی و جدی."
    else:
        mood = "متعادل و چندوجهی 🎭"
        mood_reason = "ترکیبی متعادل از شوخی و جدیت داره، بسته به فضای گفتگو رفتارش تغییر می‌کنه."

    hour_buckets = {
        "🌅 صبح (۶ تا ۱۲)": 0,
        "☀️ ظهر و عصر (۱۲ تا ۱۸)": 0,
        "🌆 غروب و شب (۱۸ تا ۲۴)": 0,
        "🌙 نیمه‌شب (۰ تا ۶)": 0,
    }
    for h, c in hour_counter.items():
        if 6 <= h < 12:
            hour_buckets["🌅 صبح (۶ تا ۱۲)"] += c
        elif 12 <= h < 18:
            hour_buckets["☀️ ظهر و عصر (۱۲ تا ۱۸)"] += c
        elif 18 <= h < 24:
            hour_buckets["🌆 غروب و شب (۱۸ تا ۲۴)"] += c
        else:
            hour_buckets["🌙 نیمه‌شب (۰ تا ۶)"] += c
    golden_hour = max(hour_buckets, key=hour_buckets.get) if total_msgs else "نامشخص"

    weekday_names = ["دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"]
    top_weekday = weekday_names[max(weekday_counter, key=weekday_counter.get)] if weekday_counter else "نامشخص"

    dominant_type = type_counter.most_common(1)[0][0] if type_counter else "text"
    type_labels = {"text": "متن‌نویسی", "sticker": "استیکر", "photo": "عکس", "video": "ویدیو", "voice": "ویس", "doc": "فایل"}

    badges = []
    if golden_hour == "🌙 نیمه‌شب (۰ تا ۶)":
        badges.append("🦉 شب‌زنده‌دار رسمی گروه")
    if dominant_type == "sticker" and type_counter["sticker"] >= total_msgs * 0.3:
        badges.append("🎭 استیکرباز حرفه‌ای")
    if quick_pct is not None and quick_pct >= 60:
        badges.append("⚡ سریع‌ترین ریپلای‌زن")
    if avg_len >= 60:
        badges.append("📚 پیام‌نویس حرفه‌ای")
    if playful_score >= calm_score * 2 and playful_score >= 5:
        badges.append("😂 شوخ‌طبع گروه")
    if not badges:
        badges.append("🌟 عضو فعال و رنگارنگ گروه")

    return {
        "total_msgs": total_msgs,
        "top_emojis": top_emojis,
        "mood": mood,
        "mood_reason": mood_reason,
        "avg_response_min": avg_response_min,
        "quick_pct": quick_pct,
        "golden_hour": golden_hour,
        "top_weekday": top_weekday,
        "dominant_type": type_labels.get(dominant_type, dominant_type),
        "avg_len": avg_len,
        "question": question,
        "badges": badges,
    }


@client.on(events.NewMessage(pattern='/dna$', func=authorized))
async def dna_cmd(event):
    chat = await event.get_chat()
    reply = await event.get_reply_message()
    target_id = reply.sender_id if reply else (event.sender_id if not event.out else owner_id)

    msg = await start_response(event, "🧬 در حال ساخت کارت DNA رفتاری...")
    await asyncio.sleep(2)

    try:
        target = await client.get_entity(target_id)
    except Exception:
        target = None

    name = (target.first_name or target.username or str(target_id)) if target else str(target_id)

    stats = await analyze_dna(chat, target_id)
    if not stats:
        try:
            await msg.edit(f"⚠️ داده‌ی کافی از {name} توی این گروه پیدا نشد.")
        except Exception:
            pass
        return

    emoji_lines = "\n".join([f"{i+1}. {e} — {c} بار" for i, (e, c) in enumerate(stats["top_emojis"])]) or "هنوز ایموجی خاصی ثبت نشده"

    response_line = (
        f"⚡ <b>سرعت پاسخ‌گویی:</b> میانگین {stats['avg_response_min']} دقیقه ({stats['quick_pct']}٪ زیر ۵ دقیقه)"
        if stats["avg_response_min"] is not None
        else "⚡ <b>سرعت پاسخ‌گویی:</b> داده‌ی کافی نیست"
    )

    badges_text = " | ".join(stats["badges"])

    text = f"""
🧬 ━━━━━━━━━━━━━━━━━━━━━ 🧬
      <b>کارت DNA رفتاری</b>
         {name}
🧬 ━━━━━━━━━━━━━━━━━━━━━ 🧬

😀 <b>۳ ایموجی محبوب:</b>
{emoji_lines}

🎭 <b>حال‌وهوای کلی:</b> {stats['mood']}
{stats['mood_reason']}

🕒 <b>ساعت طلایی فعالیت:</b> {stats['golden_hour']}
📅 <b>پرفعالیت‌ترین روز:</b> {stats['top_weekday']}
📨 <b>نوع محتوای محبوب:</b> {stats['dominant_type']}
✍️ <b>میانگین طول پیام:</b> {stats['avg_len']} کاراکتر
❓ <b>روحیه‌ی پرسشگری:</b> {stats['question']} سؤال پرسیده
{response_line}

🏅 <b>نشان‌های ویژه:</b>
{badges_text}

🧬 ━━━━━━━━━━━━━━━━━━━━━ 🧬
"""
    try:
        await msg.edit(text, parse_mode='html')
    except Exception:
        pass


@client.on(events.NewMessage(pattern='/sh$', func=authorized))
async def shootout_cmd(event):
    reply = await event.get_reply_message()
    if not reply:
        await respond(event, "⚠️ باید روی پیام شخص مورد نظر ریپلای بزنی.")
        return

    issuer_id = event.sender_id if not event.out else owner_id
    try:
        issuer = await client.get_entity(issuer_id)
        issuer_name = issuer.first_name or issuer.username or "من"
    except Exception:
        issuer_name = "من"

    try:
        target = await client.get_entity(reply.sender_id)
        target_name = target.first_name or target.username or "طرف مقابل"
    except Exception:
        target_name = "طرف مقابل"

    gap = 10
    header = f"🔵 {issuer_name}  🆚  {target_name} 🔴"

    frames = [f"{header}\n\n🔫🧍‍♂️{'· ' * gap}🧍"]
    for i in range(1, gap + 1):
        left = '· ' * (i - 1)
        right = '· ' * (gap - i)
        frames.append(f"{header}\n\n🔫🧍‍♂️{left}➤ {right}🧍")
    frames.append(f"{header}\n\n🔫🧍‍♂️{'· ' * gap}💥")
    frames.append(f"{header}\n\n🔫🧍‍♂️{'· ' * gap}🧎💫")
    frames.append(f"{header}\n\n🔫🧍‍♂️{'· ' * gap}🪦\n\n🔫 {issuer_name}: <b>کشتمت!</b> 😎")

    total_duration = 4
    interval = total_duration / len(frames)

    msg = await start_response(event, frames[0], parse_mode='html')
    for frame in frames[1:]:
        await asyncio.sleep(interval)
        try:
            await msg.edit(frame, parse_mode='html')
        except Exception:
            pass


print("در حال اتصال...")
client.start(phone=phone)
print("✅ لاگین شد. برای دیدن لیست دستورات بنویس: /help")
client.run_until_disconnected()