import asyncio
import aiohttp
import json
import os
import platform
import psutil
import random
from datetime import datetime
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, BusinessConnection
from aiogram.filters import Command
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.fsm.state import StatesGroup, State
from aiogram.fsm.context import FSMContext
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.exceptions import TelegramAPIError

# --- CONFIGURATION ---
BOT_TOKEN = "7556016387:AAE4MqpXJj8F6sfERs3y6KBjkvylR-zJ70Q"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(storage=MemoryStorage())

START_TIME = datetime.now()
CONFIG_FILE = "bot_config.json"

DEFAULT_CONFIG = {
    "admin_id": 0,
    "is_active": False,
    "mode": "ai", 
    "delay_enabled": True,
    "delay_minutes": 5,
    "keywords": {
        "سلام": ["سلام! من دستیار تونی هستم. تونی فعلاً نیست، پیامت رو بذار."]
    }
}

def load_config():
    if not os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, "w", encoding="utf-8") as f:
            json.dump(DEFAULT_CONFIG, f, ensure_ascii=False, indent=4)
        
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
        
    for key, value in DEFAULT_CONFIG.items():
        if key not in data:
            data[key] = value
            
    if "keywords" in data:
        for kw in data["keywords"]:
            if isinstance(data["keywords"][kw], str):
                data["keywords"][kw] = [data["keywords"][kw]]
                
    return data

def save_config(config_data):
    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(config_data, f, ensure_ascii=False, indent=4)

config = load_config()

# حافظه سیستم
pending_tasks = {}
active_sessions = {}
ai_conversation_memory = {}

def is_admin_msg(message: Message) -> bool:
    return message.from_user.id == config.get("admin_id", 0)

def is_admin_call(callback: CallbackQuery) -> bool:
    return callback.from_user.id == config.get("admin_id", 0)

# --- AI CONFIGURATION ---
OPENROUTER_API_KEY = "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"
AI_MODELS = [
    "nvidia/nemotron-3-super-120b-a12b:free",
    "meta-llama/llama-3.1-8b-instruct:free",
    "qwen/qwen-2-7b-instruct:free",
    "google/gemma-2-9b-it:free",
    "mistralai/mistral-7b-instruct:free"
]

SYSTEM_PROMPT = """
تو اکو (Echo) هستی، دستیار هوشمند و اختصاصی تونی (Tony). 
تونی در حال حاضر در دسترس نیست و به زودی برمی‌گردد. 
وظیفه تو این است که بسیار دوستانه و مودبانه به کاربران اطلاع دهی که تونی فعلاً نیست، اما در این مدت تو می‌توانی با آن‌ها صحبت کنی یا پیامشان را یادداشت کنی.
جواب‌هایت باید کاملاً به زبان فارسی، کوتاه، یکدست و طبیعی باشد. 
هیچ قولی از طرف تونی نده، اطلاعات ساختگی تولید نکن و از گفتن حرف‌های عجیب پرهیز کن.
"""

async def get_ai_response(chat_id, user_message):
    if chat_id not in ai_conversation_memory:
        ai_conversation_memory[chat_id] = []
        
    ai_conversation_memory[chat_id].append({"role": "user", "content": user_message})
    
    if len(ai_conversation_memory[chat_id]) > 6:
        ai_conversation_memory[chat_id] = ai_conversation_memory[chat_id][-6:]

    headers = {
        "Authorization": f"Bearer {OPENROUTER_API_KEY}",
        "HTTP-Referer": "https://tonys.ir",
        "Content-Type": "application/json"
    }
    
    messages = [{"role": "system", "content": SYSTEM_PROMPT}] + ai_conversation_memory[chat_id]
    
    # تلاش برای گرفتن پاسخ با چرخیدن روی لیست مدل‌های رایگان در صورت بروز ارور
    for model in AI_MODELS:
        data = {"model": model, "messages": messages}
        try:
            async with aiohttp.ClientSession() as session:
                async with session.post("https://openrouter.ai/api/v1/chat/completions", headers=headers, json=data) as resp:
                    result = await resp.json()
                    
                    if 'choices' in result:
                        assistant_reply = result['choices'][0]['message']['content']
                        # ذخیره در حافظه مکالمه
                        ai_conversation_memory[chat_id].append({"role": "assistant", "content": assistant_reply})
                        return assistant_reply
                    else:
                        error_details = result.get('error', result)
                        # چاپ هشدار در کنسول جهت مانیتورینگ ادمین و تلاش برای مدل بعدی
                        print(f"WARN: Model {model} is busy ({error_details.get('message', 'Rate Limit')}). Trying next free model...")
                        continue
                        
        except Exception as e:
            print(f"WARN: Connection failed for {model}: {e}. Trying next...")
            continue
            
    # اگر تمام مدل‌های لیست به بن‌بست خوردند و ارور دادند:
    return "در حال حاضر سرورهای پاسخگویی شلوغ هستند. لطفاً پیام خود را بگذارید تا در اسرع وقت پاسخ داده شود."

class BotStates(StatesGroup):
    waiting_for_delay_time = State()
    waiting_for_new_keyword = State()
    waiting_for_new_keyword_ans = State()
    waiting_for_additional_ans = State()

@dp.business_connection()
async def handle_business_connection(connection: BusinessConnection):
    global config
    if config.get("admin_id", 0) == 0:
        config["admin_id"] = connection.user.id
        save_config(config)
        print(f"SYSTEM: Auto-detected and paired Admin ID: {connection.user.id}")

def get_main_panel():
    global config
    builder = InlineKeyboardBuilder()
    status_text = "🟢 روشن کردن ربات" if not config["is_active"] else "🔴 خاموش کردن ربات"
    builder.button(text=status_text, callback_data="toggle_status")
    
    mode_text = "📝 حالت: پاسخ آماده" if config["mode"] == "normal" else "🤖 حالت: هوش مصنوعی"
    builder.button(text=mode_text, callback_data="toggle_mode")
    
    delay_toggle_text = "⏱ تاخیر: فعال" if config["delay_enabled"] else "⚡️ تاخیر: غیرفعال (درجا)"
    builder.button(text=delay_toggle_text, callback_data="toggle_delay")
    
    if config["delay_enabled"]:
        builder.button(text=f"⚙️ زمان انتظار ({config['delay_minutes']} دقیقه)", callback_data="prompt_delay")
        
    builder.button(text="🔑 مدیریت کلمات کلیدی", callback_data="manage_keywords")
    builder.button(text="📊 پایش سلامت", callback_data="server_health")
    builder.button(text="❓ راهنما", callback_data="show_help")
    builder.adjust(1, 1, 2, 1, 2)
    return builder.as_markup()

def get_keywords_panel():
    global config
    builder = InlineKeyboardBuilder()
    keywords_list = list(config["keywords"].keys())
    
    for idx, kw in enumerate(keywords_list):
        display_text = kw if len(kw) <= 15 else f"{kw[:12]}..."
        builder.button(text=f"🔸 {display_text}", callback_data=f"kw_view:{idx}")
        
    builder.adjust(2)
    control_builder = InlineKeyboardBuilder()
    control_builder.button(text="➕ افزودن کلمه جدید", callback_data="kw_add_prompt")
    control_builder.button(text="🗑 حذف همه‌چیز", callback_data="kw_clear_confirm")
    control_builder.button(text="🔙 بازگشت به پنل", callback_data="back_to_main")
    control_builder.adjust(1, 1, 1)
    builder.attach(control_builder)
    return builder.as_markup()

@dp.message(Command("start", "panel"))
async def open_panel(message: Message, state: FSMContext):
    global config
    await state.clear()
    
    if config.get("admin_id", 0) == 0:
        config["admin_id"] = message.from_user.id
        save_config(config)
        await message.reply("✅ **شما به عنوان ادمین اصلی ربات ثبت شدید!**", parse_mode="Markdown")

    if not is_admin_msg(message): return

    current_status = "فعال 🟢" if config["is_active"] else "غیرفعال 🔴"
    current_mode = "هوش مصنوعی 🤖" if config["mode"] == "ai" else "پاسخ آماده 📝"
    delay_status = f"{config['delay_minutes']} دقیقه" if config["delay_enabled"] else "غیرفعال (درجا)"
    
    text = (
        f"**👋 به پنل مدیریت پیشرفته اکو خوش آمدید**\n\n"
        f"وضعیت سیستم: **{current_status}**\n"
        f"حالت پاسخگویی: **{current_mode}**\n"
        f"تاخیر زمانی: **{delay_status}**"
    )
    await message.reply(text, reply_markup=get_main_panel(), parse_mode="Markdown")

@dp.message(Command("cancel"))
async def cancel_fsm(message: Message, state: FSMContext):
    if not is_admin_msg(message): return
    await state.clear()
    await message.reply("❌ عملیات لغو شد.")

@dp.callback_query()
async def process_callbacks(callback: CallbackQuery, state: FSMContext):
    if not is_admin_call(callback):
        await callback.answer("شما ادمین نیستید!", show_alert=True)
        return
        
    global config
    action = callback.data

    if action == "back_to_main":
        await state.clear()
        current_status = "فعال 🟢" if config["is_active"] else "غیرفعال 🔴"
        current_mode = "هوش مصنوعی 🤖" if config["mode"] == "ai" else "پاسخ آماده 📝"
        delay_status = f"{config['delay_minutes']} دقیقه" if config["delay_enabled"] else "غیرفعال (درجا)"
        text = (
            f"**👋 به پنل مدیریت پیشرفته اکو خوش آمدید**\n\n"
            f"وضعیت سیستم: **{current_status}**\n"
            f"حالت پاسخگویی: **{current_mode}**\n"
            f"تاخیر زمانی: **{delay_status}**"
        )
        await callback.message.edit_text(text, reply_markup=get_main_panel(), parse_mode="Markdown")

    elif action == "toggle_status":
        config["is_active"] = not config["is_active"]
        save_config(config)
        await callback.message.edit_reply_markup(reply_markup=get_main_panel())

    elif action == "toggle_mode":
        config["mode"] = "normal" if config["mode"] == "ai" else "ai"
        save_config(config)
        if config["mode"] == "normal":
            ai_conversation_memory.clear()
        await callback.message.edit_reply_markup(reply_markup=get_main_panel())

    elif action == "toggle_delay":
        config["delay_enabled"] = not config["delay_enabled"]
        save_config(config)
        await callback.message.edit_reply_markup(reply_markup=get_main_panel())

    elif action == "prompt_delay":
        await callback.answer()
        await callback.message.edit_text("⏱ عدد زمان تاخیر را به دقیقه بفرستید:", parse_mode="Markdown")
        await state.set_state(BotStates.waiting_for_delay_time)

    elif action == "manage_keywords":
        await callback.answer()
        await callback.message.edit_text("🔑 **لیست کلمات کلیدی ذخیره شده:**", reply_markup=get_keywords_panel(), parse_mode="Markdown")

    elif action.startswith("kw_view:"):
        await callback.answer()
        idx = int(action.split(":")[1])
        keywords_keys = list(config["keywords"].keys())
        if idx >= len(keywords_keys): return
        keyword = keywords_keys[idx]
        answers = config["keywords"][keyword]
        
        ans_text = "\n".join([f"**{i+1}.** {ans}" for i, ans in enumerate(answers)])
        text = f"🔑 کلمه: **{keyword}**\n\n📋 پاسخ‌ها (ارسال تصادفی):\n{ans_text}"
        
        builder = InlineKeyboardBuilder()
        builder.button(text="➕ اضافه کردن جواب جدید", callback_data=f"kw_add_ans:{idx}")
        builder.button(text="❌ حذف کلمه", callback_data=f"kw_del_single:{idx}")
        builder.button(text="🔙 بازگشت", callback_data="manage_keywords")
        builder.adjust(1, 1, 1)
        await callback.message.edit_text(text, reply_markup=builder.as_markup(), parse_mode="Markdown")

    elif action.startswith("kw_add_ans:"):
        await callback.answer()
        idx = int(action.split(":")[1])
        keyword = list(config["keywords"].keys())[idx]
        await state.update_data(edit_kw_idx=idx)
        await callback.message.edit_text(f"📝 پاسخ جدید برای **{keyword}** را بفرستید:")
        await state.set_state(BotStates.waiting_for_additional_ans)

    elif action.startswith("kw_del_single:"):
        idx = int(action.split(":")[1])
        keywords_keys = list(config["keywords"].keys())
        if idx < len(keywords_keys):
            removed_kw = keywords_keys[idx]
            del config["keywords"][removed_kw]
            save_config(config)
        await callback.message.edit_text("🔑 کلمه کلیدی با موفقیت حذف شد.", reply_markup=get_keywords_panel())

    elif action == "kw_add_prompt":
        await callback.answer()
        await callback.message.edit_text("➕ **کلمه کلیدی جدید** را بفرستید:")
        await state.set_state(BotStates.waiting_for_new_keyword)

    elif action == "kw_clear_confirm":
        await callback.answer()
        builder = InlineKeyboardBuilder()
        builder.button(text="💥 بله، حذف شوند", callback_data="kw_clear_execute")
        builder.button(text="🔙 خیر، بازگشت", callback_data="manage_keywords")
        builder.adjust(1, 1)
        await callback.message.edit_text("⚠️ آیا از حذف **تمامی** اطلاعات مطمئنید؟", reply_markup=builder.as_markup())

    elif action == "kw_clear_execute":
        config["keywords"] = {}
        save_config(config)
        await callback.message.edit_text("🗑 تمام کلمات کلیدی حذف شدند.", reply_markup=get_keywords_panel())

    elif action == "server_health":
        await callback.answer()
        uptime = datetime.now() - START_TIME
        hours, remainder = divmod(uptime.seconds, 3600)
        minutes, seconds = divmod(remainder, 60)
        
        memory_size = len(ai_conversation_memory)
        health_text = (
            f"**📊 گزارش سرور و سیستم:**\n\n"
            f"⏱ **بیداری ربات:** {hours} ساعت و {minutes} دقیقه\n"
            f"💻 **پردازنده (CPU):** {psutil.cpu_percent()}%\n"
            f"💾 **رم (RAM):** {psutil.virtual_memory().percent}%\n"
            f"🧠 **کاربران در حافظه AI:** {memory_size} نفر\n"
            f"🟢 **وضعیت:** کاملا پایدار"
        )
        builder = InlineKeyboardBuilder()
        builder.button(text="🔙 بازگشت به پنل", callback_data="back_to_main")
        await callback.message.edit_text(health_text, reply_markup=builder.as_markup(), parse_mode="Markdown")

    elif action == "show_help":
        await callback.answer()
        help_text = (
            "**❓ راهنمای جامع دستیار هوشمند اکو (Echo)**\n\n"
            "**۱. حالت‌های پاسخگویی:**\n"
            "🤖 **هوش مصنوعی (AI):** دستیار با شخصیت اختصاصی و **حافظه مکالمات** با کاربر چت می‌کند (تا ۶ پیام اخیر هر شخص را به یاد می‌آورد). با تغییر حالت به پاسخ آماده یا پاسخگویی خود شما، این حافظه برای آن شخص ریست می‌شود.\n"
            "📝 **پاسخ آماده (Normal):** حساس به کلمات کلیدی. می‌توانید برای یک کلمه (مثلا 'قیمت') چند جواب مختلف ثبت کنید تا ربات به صورت تصادفی یکی را بفرستد تا چت طبیعی‌تر به نظر برسد.\n\n"
            "**۲. سیستم تاخیر هوشمند (Delay):**\n"
            "⏱ اگر فعال باشد، ربات بلافاصله جواب نمی‌دهد و N دقیقه صبر می‌کند. ⚠️ **نکته مهم:** اگر در این مدت زمان، خودتان پیام کاربر را سین کنید و جواب بدهید، تایمر ربات خودکار لغو شده و دیگر دخالت نمی‌کند!\n\n"
            "**۳. دستورات کاربردی:**\n"
            "🔹 `/panel` یا `/start`: باز کردن همین داشبورد مدیریت\n"
            "🔹 `/cancel`: لغو کردن عملیات فعلی (مثلاً وقتی وسط اضافه کردن کلمه جدید پشیمان شدید)\n\n"
            "**۴. امنیت و محدودیت‌ها:**\n"
            "🔒 ربات هرگز در چت‌های گروهی، دایرکت ربات‌های دیگر، یا چت شما با خودتان (Saved Messages) پیام نمی‌دهد تا اکانت شما از سمت تلگرام محدود نشود."
        )
        builder = InlineKeyboardBuilder()
        builder.button(text="🔙 بازگشت به پنل", callback_data="back_to_main")
        await callback.message.edit_text(help_text, reply_markup=builder.as_markup(), parse_mode="Markdown")

# --- FSM INPUT TEXT PROCESSING ---
@dp.message(BotStates.waiting_for_delay_time)
async def process_delay_input(message: Message, state: FSMContext):
    if not is_admin_msg(message): return
    try:
        mins = int(message.text)
        if mins < 0: raise ValueError()
        config["delay_minutes"] = mins
        save_config(config)
        await state.clear()
        await message.reply(f"✅ زمان تاخیر روی **{mins}** دقیقه تنظیم شد.")
    except ValueError:
        await message.reply("⚠️ عدد معتبر نیست.")

@dp.message(BotStates.waiting_for_new_keyword)
async def process_new_kw_input(message: Message, state: FSMContext):
    if not is_admin_msg(message): return
    kw = message.text.strip()
    if kw in config["keywords"]:
        await message.reply("⚠️ این کلمه قبلاً ثبت شده.")
        return
    await state.update_data(new_kw_name=kw)
    await message.reply(f"کلمه **{kw}** ثبت شد.\nحالا پاسخ مربوط به این کلمه را بفرستید:")
    await state.set_state(BotStates.waiting_for_new_keyword_ans)

@dp.message(BotStates.waiting_for_new_keyword_ans)
async def process_new_kw_ans_input(message: Message, state: FSMContext):
    if not is_admin_msg(message): return
    data = await state.get_data()
    kw = data["new_kw_name"]
    config["keywords"][kw] = [message.text.strip()]
    save_config(config)
    await state.clear()
    await message.reply(f"✅ کلمه کلیدی با موفقیت ثبت شد.", reply_markup=get_keywords_panel())

@dp.message(BotStates.waiting_for_additional_ans)
async def process_additional_ans_input(message: Message, state: FSMContext):
    if not is_admin_msg(message): return
    data = await state.get_data()
    idx = data["edit_kw_idx"]
    keywords_keys = list(config["keywords"].keys())
    if idx >= len(keywords_keys): return
    keyword = keywords_keys[idx]
    config["keywords"][keyword].append(message.text.strip())
    save_config(config)
    await state.clear()
    await message.reply(f"✅ پاسخ جدید اضافه شد.", reply_markup=get_keywords_panel())

# --- AUTOMATION CHAT LOGIC ---
@dp.business_message()
async def handle_business_message(message: Message):
    admin_id = config.get("admin_id", 0)
    if admin_id == 0 or not config["is_active"]: return
    if message.chat.type != "private": return
    if message.from_user and message.from_user.is_bot: return
    if message.chat.id == admin_id: return

    if message.from_user and message.from_user.id == admin_id:
        chat_id = message.chat.id
        if chat_id in pending_tasks:
            pending_tasks[chat_id].cancel()
            del pending_tasks[chat_id]
        if chat_id in active_sessions:
            del active_sessions[chat_id]
            
        if chat_id in ai_conversation_memory:
            del ai_conversation_memory[chat_id]
            
        return

    chat_id = message.chat.id
    user_text = message.text
    if not user_text: return

    if not config["delay_enabled"] or active_sessions.get(chat_id):
        await execute_business_reply(message, user_text)
        return

    if chat_id in pending_tasks:
        pending_tasks[chat_id].cancel()

    task = asyncio.create_task(wait_and_reply(message, user_text))
    pending_tasks[chat_id] = task

async def wait_and_reply(message: Message, user_text: str):
    chat_id = message.chat.id
    try:
        await asyncio.sleep(config["delay_minutes"] * 60)
        await execute_business_reply(message, user_text)
        active_sessions[chat_id] = True
    except asyncio.CancelledError:
        pass
    finally:
        if chat_id in pending_tasks:
            del pending_tasks[chat_id]

async def execute_business_reply(message: Message, user_text: str):
    if not message.business_connection_id:
        return

    response = ""
    if config["mode"] == "normal":
        replied = False
        for keyword, answers in config["keywords"].items():
            if keyword.lower() in user_text.lower():
                response = random.choice(answers)
                replied = True
                break
                
        # اگر کلمه کلیدی مچ نشد، بدون ارسال هیچ پیامی متوقف شو
        if not replied:
            print(f"IGNORED: No keyword matched for Chat ID {message.chat.id}. Remaining silent.")
            return
            
    elif config["mode"] == "ai":
        try:
            await bot.send_chat_action(
                chat_id=message.chat.id, 
                action="typing", 
                business_connection_id=message.business_connection_id
            )
        except Exception:
            pass
            
        try:
            response = await get_ai_response(message.chat.id, user_text)
        except Exception as e:
            print(f"CRITICAL: AI Generation error: {e}")
            response = "متاسفانه ارتباط من با سرور قطع شده، لطفاً بعداً پیام بگذارید."

    # جلوگیری از ارسال پیام خالی به تلگرام
    if not response:
        return

    try:
        await bot.send_message(
            chat_id=message.chat.id,
            text=response,
            business_connection_id=message.business_connection_id
        )
        print(f"SUCCESS: Advanced reply sent to Chat ID {message.chat.id}")
    except TelegramAPIError as e:
        print(f"ERROR: Delivery failed: {str(e)}")

async def main():
    print("--------------------------------------------------")
    print("🚀 ADVANCED BOT WITH MEMORY IS RUNNING...")
    print("--------------------------------------------------")
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())