from telethon import TelegramClient, events, Button
from telethon.errors import (
    SessionPasswordNeededError, ChatAdminRequiredError,
    UserPrivacyRestrictedError, UserNotMutualContactError,
    UserChannelsTooMuchError, FloodWaitError, PeerFloodError,
    UserAlreadyParticipantError, InputUserDeactivatedError,
    ChatWriteForbiddenError, UserBannedInChannelError
)
from telethon.tl.types import (
    InputPeerUser, ChannelParticipantsAdmins,
    UserStatusOnline, UserStatusRecently, UserStatusLastWeek, UserStatusOffline
)
from telethon.tl.functions.channels import InviteToChannelRequest
from telethon.tl.functions.messages import AddChatUserRequest
import asyncio
import os
import json
from datetime import datetime, timedelta, timezone

# ================== فایل‌ها ==================
SESSIONS_FOLDER = "sessions"
LEECH_FILE = "leeched_members.json"
CONFIG_FILE = "config.json"

if not os.path.exists(SESSIONS_FOLDER):
    os.makedirs(SESSIONS_FOLDER)

# ================== متغیرهای جهانی ==================
bot = None
current_task = None
user_states = {}

# ================== توابع کمکی ==================
def load_config():
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return {"api_id": None, "api_hash": None, "bot_token": None}

def save_config(config):
    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)

def get_api():
    config = load_config()
    if config.get("api_id") and config.get("api_hash"):
        return int(config["api_id"]), config["api_hash"]
    return None, None

def load_leech():
    if os.path.exists(LEECH_FILE):
        with open(LEECH_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return []

def save_leech(data):
    with open(LEECH_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def remove_from_leech(user_id):
    data = load_leech()
    save_leech([m for m in data if m["id"] != user_id])

def is_active_within_week(user):
    status = user.status
    if isinstance(status, (UserStatusOnline, UserStatusRecently, UserStatusLastWeek)):
        return True
    if isinstance(status, UserStatusOffline) and status.was_online:
        return status.was_online >= datetime.now(timezone.utc) - timedelta(days=7)
    return False

def format_user(user):
    return {
        "id": user.id,
        "access_hash": getattr(user, "access_hash", None),
        "first_name": user.first_name or "",
        "last_name": user.last_name or "",
        "username": user.username or str(user.id),
        "phone": getattr(user, "phone", "") or "",
        "leeched_at": datetime.now().isoformat()
    }

# ================== کیبوردها ==================
def main_menu():
    return [
        [Button.inline("⚙️ تنظیمات API", b"set_api")],
        [Button.inline("➕ اد اکانت", b"add_account"), Button.inline("📂 لیست سشن‌ها", b"list_sessions")],
        [Button.inline("📥 لیچ گروه", b"leech_group"), Button.inline("👥 اد گروه", b"add_group")],
        [Button.inline("🗑️ حذف لیچ", b"delete_leech"), Button.inline("📊 آمار لیچ", b"leech_stats")]
    ]

def stop_button():
    return [[Button.inline("⏹ پایان عملیات", b"stop_operation")]]

async def show_main_menu(event, text="🏠 منوی اصلی:"):
    await event.respond(text, buttons=main_menu())

# ================== هندلر دکمه‌ها ==================
@events.register(events.CallbackQuery)
async def callback_handler(event):
    global current_task
    data = event.data.decode("utf-8")
    user_id = event.sender_id

    # ----- توقف عملیات -----
    if data == "stop_operation":
        if current_task and not current_task.done():
            current_task.cancel()
            current_task = None
            await event.answer("⏹ عملیات متوقف شد", alert=True)
            try:
                await event.edit("⏹ عملیات توسط شما متوقف شد.\n\n🏠 به منوی اصلی برگشتید.", buttons=main_menu())
            except:
                await event.respond("⏹ عملیات توسط شما متوقف شد.", buttons=main_menu())
        else:
            await event.answer("ℹ️ هیچ عملیاتی در حال اجرا نیست", alert=True)
        return

    if current_task and not current_task.done():
        await event.answer("⚠️ اول با دکمه «پایان عملیات» عملیات فعلی را متوقف کنید", alert=True)
        return

    # ----- تنظیمات API -----
    if data == "set_api":
        user_states[user_id] = "waiting_api_id"
        await event.edit("⚙️ لطفاً **API ID** را ارسال کنید:")

    # ----- اد اکانت -----
    elif data == "add_account":
        api_id, api_hash = get_api()
        if not api_id:
            await event.answer("⚠️ اول از دکمه تنظیمات API مقادیر را وارد کنید", alert=True)
            return
        user_states[user_id] = "waiting_phone"
        await event.edit("📱 شماره تلفن را با کد کشور ارسال کنید:\nمثال: `+989123456789`")

    # ----- لیست سشن‌ها -----
    elif data == "list_sessions":
        files = [f for f in os.listdir(SESSIONS_FOLDER) if f.endswith(".session")]
        if not files:
            text = "📂 هیچ سشنی وجود ندارد."
        else:
            text = "📂 سشن‌های موجود:\n\n" + "\n".join(f"🔹 `{f}`" for f in files)
        await event.edit(text, buttons=main_menu())

    # ----- آمار لیچ -----
    elif data == "leech_stats":
        count = len(load_leech())
        await event.edit(f"📊 تعداد اعضای باقی‌مانده در لیست لیچ: **{count}**", buttons=main_menu())

    # ----- حذف لیچ -----
    elif data == "delete_leech":
        save_leech([])
        await event.edit("🗑️ لیست لیچ با موفقیت کامل پاک شد.", buttons=main_menu())

    # ----- لیچ گروه -----
    elif data == "leech_group":
        api_id, api_hash = get_api()
        if not api_id:
            await event.answer("⚠️ اول تنظیمات API را انجام دهید", alert=True)
            return
        sessions = [f for f in os.listdir(SESSIONS_FOLDER) if f.endswith(".session")]
        if not sessions:
            await event.answer("📂 هیچ سشنی وجود ندارد", alert=True)
            return
        buttons = [[Button.inline(f"🔹 {s}", f"leech_sess_{i}".encode())] for i, s in enumerate(sessions)]
        buttons.append([Button.inline("🔙 بازگشت", b"back_main")])
        user_states[user_id] = {"state": "leech_choose_session", "sessions": sessions}
        await event.edit("📥 سشن مورد نظر برای لیچ را انتخاب کنید:", buttons=buttons)

    elif data.startswith("leech_sess_"):
        idx = int(data.split("_")[-1])
        sessions = [f for f in os.listdir(SESSIONS_FOLDER) if f.endswith(".session")]
        session_file = sessions[idx]
        user_states[user_id] = {"state": "leech_choose_method", "session": session_file}
        buttons = [
            [Button.inline("👥 از طریق ممبر لیست", b"leech_method_members")],
            [Button.inline("💬 از طریق پیام‌ها", b"leech_method_messages")],
            [Button.inline("🔙 بازگشت", b"back_main")]
        ]
        await event.edit(f"🔹 سشن انتخاب شد: `{session_file}`\n\n📥 روش لیچ را انتخاب کنید:", buttons=buttons)

    elif data == "leech_method_members":
        user_states[user_id]["state"] = "leech_waiting_group_members"
        await event.edit("👥 یوزرنیم / لینک / آیدی گروه مبدا را ارسال کنید:")

    elif data == "leech_method_messages":
        user_states[user_id]["state"] = "leech_waiting_days"
        await event.edit("📅 تا چند روز پیش اسکن شود؟\nفقط عدد بفرستید (مثال: ۱۰)")

    # ----- اد گروه -----
    elif data == "add_group":
        api_id, api_hash = get_api()
        if not api_id:
            await event.answer("⚠️ اول تنظیمات API را انجام دهید", alert=True)
            return
        if not load_leech():
            await event.answer("📭 لیست لیچ خالی است", alert=True)
            return
        user_states[user_id] = {"state": "add_waiting_target"}
        await event.edit(
            "👥 **گروه مقصد** را ارسال کنید (آیدی / یوزرنیم / لینک):\n\n"
            "⚠️ اکانتی که باهاش اد می‌کنی باید تو گروه مقصد ادمین باشه و دسترسی Add Members داشته باشه.\n"
            "🔒 خیلی از کاربرها تنظیمات حریم خصوصی‌شون رو روی «هیچ‌کس نتونه منو به گروه اضافه کنه» گذاشتن → این‌ها اد نمی‌شن."
        )

    elif data == "back_main":
        user_states.pop(user_id, None)
        await event.edit("🏠 منوی اصلی:", buttons=main_menu())

# ================== دریافت پیام‌های متنی ==================
@events.register(events.NewMessage)
async def message_handler(event):
    global current_task
    if not event.is_private:
        return

    user_id = event.sender_id
    text = event.raw_text.strip()
    state = user_states.get(user_id)

    if not state:
        if text == "/start":
            await show_main_menu(event, "👋 سلام! به ربات مدیریت سشن و لیچ خوش آمدید.")
        return

    # ----- تنظیم API -----
    if state == "waiting_api_id":
        try:
            api_id = int(text)
            user_states[user_id] = {"state": "waiting_api_hash", "api_id": api_id}
            await event.respond("🔑 حالا **API HASH** را ارسال کنید:")
        except:
            await event.respond("⚠️ API ID باید عدد باشد. دوباره ارسال کنید:")

    elif isinstance(state, dict) and state.get("state") == "waiting_api_hash":
        config = load_config()
        config["api_id"] = state["api_id"]
        config["api_hash"] = text
        save_config(config)
        user_states.pop(user_id, None)
        await event.respond("✅ تنظیمات API با موفقیت ذخیره شد.", buttons=main_menu())

    # ----- اد اکانت -----
    elif state == "waiting_phone":
        api_id, api_hash = get_api()
        session_name = text.replace("+", "").replace(" ", "")
        session_path = os.path.join(SESSIONS_FOLDER, session_name)
        client = TelegramClient(session_path, api_id, api_hash)
        await client.connect()
        try:
            await client.send_code_request(text)
            user_states[user_id] = {"state": "waiting_code", "phone": text, "client": client}
            await event.respond("📨 کد تأیید را ارسال کنید:")
        except Exception as e:
            await client.disconnect()
            user_states.pop(user_id, None)
            await event.respond(f"❌ خطا در ارسال کد:\n`{e}`", buttons=main_menu())

    elif isinstance(state, dict) and state.get("state") == "waiting_code":
        client = state["client"]
        try:
            await client.sign_in(state["phone"], text)
            me = await client.get_me()
            await event.respond(f"✅ سشن با موفقیت ساخته شد\n👤 نام: {me.first_name}\n🆔 آیدی: `{me.id}`", buttons=main_menu())
            await client.disconnect()
            user_states.pop(user_id, None)
        except SessionPasswordNeededError:
            user_states[user_id]["state"] = "waiting_password"
            await event.respond("🔐 رمز دو مرحله‌ای را ارسال کنید:")
        except Exception as e:
            await client.disconnect()
            user_states.pop(user_id, None)
            await event.respond(f"❌ خطا:\n`{e}`", buttons=main_menu())

    elif isinstance(state, dict) and state.get("state") == "waiting_password":
        client = state["client"]
        try:
            await client.sign_in(password=text)
            me = await client.get_me()
            await event.respond(f"✅ سشن با موفقیت ساخته شد\n👤 نام: {me.first_name}", buttons=main_menu())
        except Exception as e:
            await event.respond(f"❌ خطا:\n`{e}`", buttons=main_menu())
        await client.disconnect()
        user_states.pop(user_id, None)

    # ----- لیچ (تعداد روز) -----
    elif isinstance(state, dict) and state.get("state") == "leech_waiting_days":
        try:
            days = int(text)
            if days <= 0:
                raise ValueError
            user_states[user_id]["days"] = days
            user_states[user_id]["state"] = "leech_waiting_group_messages"
            await event.respond("👥 یوزرنیم / لینک / آیدی گروه مبدا را ارسال کنید:")
        except:
            await event.respond("⚠️ فقط یک عدد معتبر ارسال کنید (مثال: ۱۰):")

    # ----- شروع لیچ -----
    elif isinstance(state, dict) and state.get("state") in ["leech_waiting_group_members", "leech_waiting_group_messages"]:
        group = text
        session_file = state["session"]
        api_id, api_hash = get_api()
        session_path = os.path.join(SESSIONS_FOLDER, session_file.replace(".session", ""))
        method = state["state"]

        msg = await event.respond("⏳ در حال لیچ کردن... لطفاً صبر کنید.", buttons=stop_button())

        async def do_leech():
            global current_task
            client = TelegramClient(session_path, api_id, api_hash)
            try:
                await client.start()
                entity = await client.get_entity(group)
                leeched = load_leech()
                existing = {m["id"] for m in leeched}
                new_count = 0

                if method == "leech_waiting_group_members":
                    async for user in client.iter_participants(entity):
                        if current_task.cancelled():
                            raise asyncio.CancelledError()
                        if user.bot or not is_active_within_week(user):
                            continue
                        if user.id in existing:
                            continue
                        leeched.append(format_user(user))
                        existing.add(user.id)
                        new_count += 1
                    save_leech(leeched)
                    await msg.edit(f"✅ لیچ از ممبر لیست تمام شد\n🆕 جدید: {new_count}\n📊 کل: {len(load_leech())}", buttons=main_menu())
                else:
                    days = state["days"]
                    offset = datetime.now(timezone.utc) - timedelta(days=days)
                    new_users = {}
                    async for message in client.iter_messages(entity, offset_date=offset):
                        if current_task.cancelled():
                            raise asyncio.CancelledError()
                        if not message.sender_id:
                            continue
                        try:
                            sender = message.sender or await client.get_entity(message.sender_id)
                        except:
                            continue
                        if getattr(sender, "bot", False):
                            continue
                        if sender.id in existing or sender.id in new_users:
                            continue
                        new_users[sender.id] = format_user(sender)
                        existing.add(sender.id)
                    leeched.extend(new_users.values())
                    save_leech(leeched)
                    await msg.edit(f"✅ لیچ از پیام‌ها تمام شد\n🆕 جدید: {len(new_users)}\n📊 کل: {len(load_leech())}", buttons=main_menu())

            except asyncio.CancelledError:
                await msg.edit("⏹ لیچ توسط شما متوقف شد.", buttons=main_menu())
            except Exception as e:
                await msg.edit(f"❌ خطا در لیچ:\n`{e}`", buttons=main_menu())
            finally:
                try:
                    await client.disconnect()
                except:
                    pass
                user_states.pop(user_id, None)
                current_task = None

        current_task = asyncio.create_task(do_leech())

    # ----- اد گروه -----
    elif isinstance(state, dict) and state.get("state") == "add_waiting_target":
        user_states[user_id]["target"] = text
        user_states[user_id]["state"] = "add_waiting_per_session"
        sessions_count = len([f for f in os.listdir(SESSIONS_FOLDER) if f.endswith(".session")])
        await event.respond(f"📂 تعداد سشن‌های موجود: **{sessions_count}**\n\n🔢 هر سشن چند نفر اد کند؟ (فقط عدد)")

    elif isinstance(state, dict) and state.get("state") == "add_waiting_per_session":
        try:
            per = int(text)
            if per <= 0:
                raise ValueError
            user_states[user_id]["per_session"] = per
            user_states[user_id]["state"] = "add_waiting_delay"
            await event.respond(
                "⏱️ **راهنمای تاخیر:**\n"
                "• عدد کمتر (۳) → سریع‌تر ولی خطر فلود بیشتر\n"
                "• عدد بیشتر (۸ یا ۱۰) → امن‌تر و کمتر فلود\n"
                "• پیشنهاد: ۵ تا ۷ ثانیه\n\n"
                "تاخیر بین هر اد چند ثانیه باشد؟"
            )
        except:
            await event.respond("⚠️ فقط یک عدد معتبر ارسال کنید:")

    elif isinstance(state, dict) and state.get("state") == "add_waiting_delay":
        try:
            delay = int(text)
            if delay < 1:
                delay = 5
        except:
            delay = 5

        target = state["target"]
        per_session = state["per_session"]
        user_states.pop(user_id, None)

        msg = await event.respond(
            f"🚀 شروع عملیات اد گروه...\n"
            f"👥 هر سشن: {per_session} نفر\n"
            f"⏱️ تاخیر: {delay} ثانیه\n\n"
            f"برای توقف از دکمه زیر استفاده کنید.",
            buttons=stop_button()
        )

        async def do_add():
            global current_task
            api_id, api_hash = get_api()
            sessions = [f for f in os.listdir(SESSIONS_FOLDER) if f.endswith(".session")]
            total_success = total_failed = total_skipped = 0

            try:
                for idx, session_file in enumerate(sessions):
                    if current_task.cancelled():
                        raise asyncio.CancelledError()

                    data = load_leech()
                    if not data:
                        break

                    batch = data[:per_session]
                    session_path = os.path.join(SESSIONS_FOLDER, session_file.replace(".session", ""))
                    client = TelegramClient(session_path, api_id, api_hash)

                    try:
                        await client.start()
                        entity = await client.get_entity(target)
                    except Exception as e:
                        total_failed += len(batch)
                        try:
                            await client.disconnect()
                        except:
                            pass
                        continue

                    for member in batch:
                        if current_task.cancelled():
                            raise asyncio.CancelledError()

                        try:
                            username = member.get("username", "")
                            if username and not str(username).isdigit():
                                user_entity = await client.get_entity(username)
                            else:
                                access_hash = member.get("access_hash")
                                if access_hash:
                                    user_entity = InputPeerUser(member["id"], int(access_hash))
                                else:
                                    user_entity = await client.get_entity(member["id"])

                            if getattr(entity, "megagroup", False) or getattr(entity, "broadcast", False):
                                await client(InviteToChannelRequest(entity, [user_entity]))
                            else:
                                await client(AddChatUserRequest(entity.id, user_entity, fwd_limit=10))

                            total_success += 1
                            remove_from_leech(member["id"])
                        except UserAlreadyParticipantError:
                            total_skipped += 1
                            remove_from_leech(member["id"])
                        except FloodWaitError as e:
                            await asyncio.sleep(e.seconds + 3)
                            continue
                        except PeerFloodError:
                            break
                        except Exception:
                            total_failed += 1

                        await asyncio.sleep(delay)

                    try:
                        await client.disconnect()
                    except:
                        pass

                    try:
                        await msg.edit(
                            f"⏳ در حال اجرا...\n"
                            f"🔹 سشن {idx+1}/{len(sessions)}\n"
                            f"✅ موفق: {total_success}\n"
                            f"⏭ قبلاً عضو: {total_skipped}\n"
                            f"❌ ناموفق: {total_failed}\n"
                            f"📊 باقی‌مانده: {len(load_leech())}",
                            buttons=stop_button()
                        )
                    except:
                        pass

                await msg.edit(
                    f"✅ عملیات با موفقیت تمام شد\n\n"
                    f"✅ موفق: {total_success}\n"
                    f"⏭ قبلاً عضو: {total_skipped}\n"
                    f"❌ ناموفق: {total_failed}\n"
                    f"📊 باقی‌مانده در لیست: {len(load_leech())}",
                    buttons=main_menu()
                )

            except asyncio.CancelledError:
                await msg.edit(
                    f"⏹ عملیات متوقف شد\n\n"
                    f"✅ موفق تا این لحظه: {total_success}\n"
                    f"📊 باقی‌مانده: {len(load_leech())}",
                    buttons=main_menu()
                )
            finally:
                current_task = None

        current_task = asyncio.create_task(do_add())

# ================== اجرای ربات ==================
async def main():
    global bot
    config = load_config()

    if not config.get("bot_token"):
        print("=" * 40)
        print("🚀 اولین اجرا - فقط یک بار توکن لازم است")
        print("=" * 40)
        token = input("Bot Token را وارد کنید: ").strip()
        config["bot_token"] = token
        save_config(config)
        print("✅ توکن ذخیره شد.\n")

    api_id = config.get("api_id") or 6
    api_hash = config.get("api_hash") or "eb06d4abfb49dc3eeb1aeb98ae0f581e"

    bot = TelegramClient("bot_session", int(api_id), api_hash)
    await bot.start(bot_token=config["bot_token"])
    print("✅ ربات با موفقیت روشن شد.")
    print("📱 برو داخل ربات /start بزن و اول «تنظیمات API» را انجام بده.")
    await bot.run_until_disconnected()

if __name__ == "__main__":
    asyncio.run(main())