import asyncio
import os
import sys
import subprocess
import time
import urllib.parse
import urllib.request
from concurrent.futures import ThreadPoolExecutor

# ─── مسیر پروژه ────────────────────────────────────────────
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
REQ_FILE = os.path.join(BASE_DIR, "requirements.txt")

sys.path.append(BASE_DIR)
try:
    from core.config import config
except ImportError:
    config = None

# برای ریکوئست‌های غیرهمگام با استفاده از ماژول استاندارد
executor = ThreadPoolExecutor(max_workers=2)

# ─── ارسال نوتیف تلگرام ────────────────────────────────────
def _sync_send_alert(message: str):
    if not config or not config.bot_token or not getattr(config, 'admin_group_id', None):
        return
    url = f"https://api.telegram.org/bot{config.bot_token}/sendMessage"
    data = urllib.parse.urlencode({
        "chat_id": config.admin_group_id,
        "text": f"⚠️ <b>[Multi-Runner System]</b>\n\n{message}",
        "parse_mode": "HTML"
    }).encode("utf-8")
    try:
        req = urllib.request.Request(url, data=data)
        urllib.request.urlopen(req, timeout=10)
    except Exception as e:
        print(f"[Runner] Telegram alert failed: {e}")

async def send_telegram_alert(message: str):
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(executor, _sync_send_alert, message)


# ─── چک و نصب requirements ─────────────────────────────────
def check_and_install_requirements():
    if not os.path.exists(REQ_FILE):
        print(f"[Runner] requirements.txt not found. Continuing...")
        return

    print(f"[Runner] Checking required packages from {REQ_FILE} ...")

    missing = []
    with open(REQ_FILE, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            pkg_name = line.split(">=")[0].split("==")[0].split("<=")[0].split("!=")[0].strip()
            try:
                result = subprocess.run(
                    [sys.executable, "-m", "pip", "show", pkg_name],
                    capture_output=True, text=True
                )
                if result.returncode != 0:
                    missing.append(line)
                    print(f"  [!] Missing package: {pkg_name}")
                else:
                    print(f"  [✓] Found package: {pkg_name}")
            except Exception as e:
                print(f"  [?] Error checking {pkg_name}: {e}")
                missing.append(line)

    if not missing:
        print("[Runner] ✅ All packages are installed.\n")
        return

    print(f"\n[Runner] Installing {len(missing)} missing packages...")
    result = subprocess.run(
        [sys.executable, "-m", "pip", "install"] + missing + ["--quiet"],
        capture_output=True, text=True
    )
    if result.returncode == 0:
        print("[Runner] ✅ Installation successful.\n")
    else:
        print(f"[Runner] ⚠️ Installation error:\n{result.stderr}\n")


# ─── مانیتورینگ ربات‌ها به صورت جداگانه ──────────────────────
async def monitor_bot(script_name: str, display_name: str):
    script_path = os.path.join(BASE_DIR, script_name)
    if not os.path.exists(script_path):
        msg = f"❌ <b>اسکریپت پیدا نشد:</b> <code>{script_name}</code>"
        print(f"[Runner] {msg}")
        await send_telegram_alert(msg)
        return

    print(f"[Runner] Starting {display_name} ({script_name})...")
    await send_telegram_alert(f"🚀 ربات <b>{display_name}</b> با موفقیت راه‌اندازی شد.")

    restart_count = 0
    last_restart_time = time.time()

    while True:
        print(f"[{display_name}] Process started.")
        env = os.environ.copy()
        env["PYTHONIOENCODING"] = "utf-8"

        process = await asyncio.create_subprocess_exec(
            sys.executable, script_path,
            env=env
        )
        
        exit_code = await process.wait()
        current_time = time.time()
        print(f"[{display_name}] Exited with code {exit_code}")

        if current_time - last_restart_time < 60:
            restart_count += 1
        else:
            restart_count = 1
        last_restart_time = current_time

        alert_msg = f"❌ <b>ربات متوقف شد!</b>\nنام: <b>{display_name}</b>\nاسکریپت: <code>{script_name}</code>\nکد خروجی: <code>{exit_code}</code>\n"

        if restart_count >= 5:
            alert_msg += (
                "🚨 <b>خطای بحرانی:</b> ربات بیش از ۵ بار در یک دقیقه کرش کرده!\n"
                "جهت جلوگیری از loop، ۶۰ ثانیه صبر می‌کنیم..."
            )
            print(f"[{display_name}] CRITICAL: Crashed too many times. Sleeping 60s...")
            await send_telegram_alert(alert_msg)
            await asyncio.sleep(60)
            restart_count = 0
        else:
            alert_msg += "🔄 راه‌اندازی مجدد در ۵ ثانیه..."
            await send_telegram_alert(alert_msg)
            print(f"[{display_name}] Restarting in 5 seconds...")
            await asyncio.sleep(5)


# ─── تابع اصلی ─────────────────────────────────────────────
async def main():
    print("=" * 52)
    print("   Advanced Multi-Bot Runner System")
    print("=" * 52)

    # نصب پکیج‌ها قبل از اجرای هر چیزی
    check_and_install_requirements()

    # تعریف ربات‌ها برای اجرا
    bots = [
        {"script": "main.py", "name": "Store Bot (فروشگاه)"},
        {"script": "admin_channel.py", "name": "Channel Assistant (دستیار کانال)"}
    ]

    print("[Runner] در حال راه‌اندازی ربات‌ها...")
    await send_telegram_alert("سیستم مانیتورینگ Multi-Runner راه‌اندازی شد. در حال استارت ربات‌ها... 🚀")

    # ساخت تسک‌ها برای اجرای همزمان همه ربات‌ها
    tasks = []
    for bot in bots:
        tasks.append(asyncio.create_task(monitor_bot(bot["script"], bot["name"])))

    # انتظار برای تسک‌ها (در واقع حلقه بی‌نهایت)
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n[Runner] Shutting down gracefully...")
        # Since processes are managed by asyncio subprocesses, cancelling tasks stops wait()
        # but the actual subprocesses might be orphaned if we don't kill them manually.
        # However, for typical usage via PM2 or manual kill, Windows handles it mostly.
