"""Brain handler — Advanced learning system with categories, search, and smart matching."""
from __future__ import annotations

import asyncio
import json
import logging
import random
import time

from telegram import Update
from telegram.constants import ParseMode
from telegram.error import BadRequest, Forbidden
from telegram.ext import ContextTypes

from bot import db
from bot.decorators import admin_only
from bot.persian_router import register_mixed

logger = logging.getLogger(__name__)


# ═══════════════════════════════════════════════
#  Learn Command
# ═══════════════════════════════════════════════

@admin_only
async def learn_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Learn a response:
    - یادبگیر [trigger] = [response]
    - یادبگیر [trigger] = [response] --cat=category --weight=2 --delay=1
    - Reply to message + یادبگیر [trigger]
    """
    if not update.message:
        return
    chat = update.effective_chat
    msg = update.message
    text = msg.text or ""
    user_id = update.effective_user.id if update.effective_user else 0

    # Reply-based: learn from replied message
    if msg.reply_to_message and "=" not in text.split("--")[0]:
        replied = msg.reply_to_message
        parts = text.split(maxsplit=1)
        if len(parts) < 2:
            await msg.reply_text(
                "📝 Usage:\n"
                "Reply to a message + یادبگیر [trigger]\n"
                "Or: یادبگیر [trigger] = [response]"
            )
            return

        trigger = parts[1].strip().lower()
        response = replied.text or replied.caption or ""
        media_type = "text"
        file_id = ""

        if replied.sticker:
            media_type, file_id = "sticker", replied.sticker.file_id
        elif replied.photo:
            media_type, file_id = "photo", replied.photo[-1].file_id
        elif replied.voice:
            media_type, file_id = "voice", replied.voice.file_id
        elif replied.video:
            media_type, file_id = "video", replied.video.file_id
        elif replied.animation:
            media_type, file_id = "animation", replied.animation.file_id

        if not response and not file_id:
            await msg.reply_text("This message has no content to learn.")
            return

        resp = await db.brain.add(
            chat.id, trigger, response or file_id,
            media_type=media_type, file_id=file_id,
            created_by=user_id,
        )
        icon = {"sticker": "📦", "photo": "📸", "voice": "🎤", "video": "🎬"}.get(media_type, "💬")
        await msg.reply_text(f"{icon} Learned: «{trigger}»")
        return

    # Inline: یادبگیر [trigger] = [response]
    if "=" in text:
        # Parse flags
        weight, delay, category = 1.0, 0.0, "general"
        import re as _re
        wm = _re.search(r'--weight=(\d+\.?\d*)', text)
        if wm:
            weight = max(0.1, min(10.0, float(wm.group(1))))
            text = text.replace(wm.group(0), "")
        dm = _re.search(r'--delay=(\d+\.?\d*)', text)
        if dm:
            delay = max(0.0, min(5.0, float(dm.group(1))))
            text = text.replace(dm.group(0), "")
        cm = _re.search(r'--cat=(\S+)', text)
        if cm:
            category = cm.group(1).strip().lower()
            text = text.replace(cm.group(0), "")

        parts = text.split("=", 1)
        if len(parts) < 2:
            await msg.reply_text("Usage: یادبگیر [trigger] = [response]")
            return

        cmd_part = parts[0].strip()
        response = parts[1].strip()
        cmd_words = cmd_part.split(maxsplit=1)
        trigger = cmd_words[1].strip().lower() if len(cmd_words) > 1 else ""

        if not trigger or not response:
            await msg.reply_text("Both trigger and response are required.")
            return

        resp = await db.brain.add(
            chat.id, trigger, response,
            category=category, weight=weight, delay=delay,
            created_by=user_id,
        )

        flags = []
        if category != "general":
            flags.append(f"📁 {category}")
        if weight != 1.0:
            flags.append(f"⚖️ {weight}x")
        if delay > 0:
            flags.append(f"⏱ {delay}s")
        flag_str = " | ".join(flags)

        await msg.reply_text(
            f"✅ Learned: «{trigger}»\n"
            f"   → {response[:80]}\n"
            f"{'   ' + flag_str if flag_str else ''}"
        )
        return

    # Help
    await msg.reply_text(
        "🧠 <b>Advanced Brain System</b>\n\n"
        "<b>Learn:</b>\n"
        "• <code>یادبگیر [trigger] = [response]</code>\n"
        "• Reply + <code>یادبگیر [trigger]</code>\n\n"
        "<b>Flags:</b>\n"
        "• <code>--cat=name</code> — category\n"
        "• <code>--weight=2</code> — priority (1-10)\n"
        "• <code>--delay=1</code> — delay seconds\n\n"
        "<b>Manage:</b>\n"
        "• <code>فراموش کن [trigger]</code>\n"
        "• <code>حذف脑 [id]</code> — delete by ID\n"
        "• <code>لیست脑</code> — list all\n"
        "• <code>جستجو脑 [query]</code> — search\n"
        "• <code>آمار脑</code> — statistics\n"
        "• <code>دسته脑</code> — list categories\n"
        "• <code>پاک脑</code> — delete all\n\n"
        "<b>Templates:</b> {user} {name} {username} {chat} {time} {day} {random} {reaction}\n"
        "<b>Multi:</b> response1 || response2 || response3",
        parse_mode=ParseMode.HTML,
    )


# ═══════════════════════════════════════════════
#  Unlearn
# ═══════════════════════════════════════════════

@admin_only
async def unlearn_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    parts = (update.message.text or "").split(maxsplit=1)
    if len(parts) < 2:
        await update.message.reply_text("Usage: فراموش کن [trigger]")
        return
    trigger = parts[1].strip().lower()
    count = await db.brain.remove(update.effective_chat.id, trigger)
    await update.message.reply_text(f"🗑 Deleted {count} response(s) for «{trigger}»")


@admin_only
async def unlearn_id_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    parts = (update.message.text or "").split()
    if len(parts) < 2:
        await update.message.reply_text("Usage: حذف脑 [id]")
        return
    try:
        resp_id = int(parts[1])
    except ValueError:
        await update.message.reply_text("ID must be a number.")
        return
    count = await db.brain.remove(update.effective_chat.id, "", response_id=resp_id)
    if count:
        await update.message.reply_text(f"✅ Deleted response #{resp_id}")
    else:
        await update.message.reply_text("Not found.")


# ═══════════════════════════════════════════════
#  List
# ═══════════════════════════════════════════════

async def list_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    chat_id = update.effective_chat.id
    responses = await db.brain.get_all(chat_id)

    if not responses:
        await update.message.reply_text("📭 Nothing learned yet.")
        return

    # Group by category
    groups: dict[str, list] = {}
    for r in responses:
        groups.setdefault(r.category, []).append(r)

    text = f"🧠 <b>Brain:</b> {len(responses)} responses in {len(groups)} categories\n\n"
    for cat, items in sorted(groups.items()):
        text += f"📁 <b>{cat}</b> ({len(items)})\n"
        for r in items[:3]:
            media_icon = {"sticker": "📦", "photo": "📸", "voice": "🎤", "video": "🎬"}.get(r.media_type, "💬")
            text += f"  {media_icon} #{r.id} «{r.trigger[:20]}» → {r.response[:30]}\n"
        if len(items) > 3:
            text += f"  ... and {len(items) - 3} more\n"

    text += "\n🔍 <code>جستجو脑 [query]</code> to search"
    await update.message.reply_text(text, parse_mode=ParseMode.HTML)


# ═══════════════════════════════════════════════
#  Search
# ═══════════════════════════════════════════════

async def search_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    parts = (update.message.text or "").split(maxsplit=1)
    if len(parts) < 2:
        await update.message.reply_text("Usage: جستجو脑 [query]")
        return

    query = parts[1].strip()
    results = await db.brain.search(update.effective_chat.id, query)

    if not results:
        await update.message.reply_text(f"🔍 No results for «{query}»")
        return

    text = f"🔍 <b>Results for «{query}»:</b>\n\n"
    for r in results[:15]:
        media_icon = {"sticker": "📦", "photo": "📸", "voice": "🎤", "video": "🎬"}.get(r.media_type, "💬")
        text += f"{media_icon} #{r.id} «{r.trigger[:25]}» → {r.response[:40]} ({r.uses_count}x)\n"

    if len(results) > 15:
        text += f"\n... and {len(results) - 15} more"

    await update.message.reply_text(text, parse_mode=ParseMode.HTML)


# ═══════════════════════════════════════════════
#  Stats
# ═══════════════════════════════════════════════

async def stats_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    stats = await db.brain.get_stats(update.effective_chat.id)

    text = (
        f"📊 <b>Brain Statistics</b>\n\n"
        f"📝 Total responses: {stats['total']}\n"
        f"📁 Categories: {stats['categories']}\n"
        f"📸 Media responses: {stats['media']}\n"
    )
    if stats["most_used"]:
        text += f"🔥 Most used: «{stats['most_used']['trigger_text']}» ({stats['most_used']['uses_count']}x)\n"

    await update.message.reply_text(text, parse_mode=ParseMode.HTML)


# ═══════════════════════════════════════════════
#  Categories
# ═══════════════════════════════════════════════

async def categories_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    cats = await db.brain.get_categories(update.effective_chat.id)

    if not cats:
        await update.message.reply_text("No categories yet.")
        return

    text = "📁 <b>Categories:</b>\n\n"
    for c in cats:
        text += f"• <b>{c['category']}</b> — {c['cnt']} responses\n"

    await update.message.reply_text(text, parse_mode=ParseMode.HTML)


# ═══════════════════════════════════════════════
#  Clear all
# ═══════════════════════════════════════════════

@admin_only
async def clear_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    text = (update.message.text or "").strip().lower()
    if "تأیید" not in text and "confirm" not in text:
        await update.message.reply_text(
            "⚠️ All learned responses will be deleted!\n"
            "Confirm: پاک脑 تأیید"
        )
        return
    count = await db.brain.remove_all(update.effective_chat.id)
    await update.message.reply_text(f"🧹 Deleted all {count} responses.")


# ═══════════════════════════════════════════════
#  Import / Export
# ═══════════════════════════════════════════════

@admin_only
async def export_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message:
        return
    data = await db.brain.export_chat(update.effective_chat.id)
    if not data:
        await update.message.reply_text("Nothing to export.")
        return
    await update.message.reply_text(
        f"📦 Export: {len(data)} responses\n\n"
        "Send this to another chat with /import脑 to import."
    )
    # Send as file
    import io
    content = json.dumps(data, ensure_ascii=False, indent=2)
    file = io.BytesIO(content.encode("utf-8"))
    file.name = f"brain_{update.effective_chat.id}_{int(time.time())}.json"
    await update.message.reply_document(document=file)


@admin_only
async def import_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message or not update.message.document:
        await update.message.reply_text("Reply to a JSON file with /import脑")
        return

    try:
        file = await context.bot.get_file(update.message.document.file_id)
        content = await file.download_as_bytearray()
        data = json.loads(content.decode("utf-8"))
        count = await db.brain.import_chat(
            update.effective_chat.id, data,
            created_by=update.effective_user.id,
        )
        await update.message.reply_text(f"✅ Imported {count} responses.")
    except Exception as e:
        await update.message.reply_text(f"❌ Import failed: {e}")


# ═══════════════════════════════════════════════
#  Check learned (called from listener)
# ═══════════════════════════════════════════════

async def check_learned(update: Update, context: ContextTypes.DEFAULT_TYPE) -> bool:
    """Check if message matches a learned response. Returns True if handled."""
    message = update.message
    if not message or not message.text:
        return False

    chat_id = update.effective_chat.id
    uid = update.effective_user.id if update.effective_user else 0

    # Skip private chats
    if update.effective_chat.type == "private":
        return False

    # Save to context
    await db.brain.remember(chat_id, uid, message.text)

    # Cooldown
    if await db.brain.check_cooldown(chat_id, "_global", 3):
        return False

    # Find match
    result = await db.brain.match(chat_id, message.text)
    if not result:
        return False

    # Per-trigger cooldown
    if await db.brain.check_cooldown(chat_id, result.response.trigger, 8):
        return False

    # Apply template
    response = db.brain.apply_template(
        result.response.response,
        user=update.effective_user,
        chat=update.effective_chat,
    )
    r = result.response

    # Multi-response
    if "||" in response and r.media_type == "text":
        response = random.choice([p.strip() for p in response.split("||")])

    # Delay
    if r.delay > 0:
        await asyncio.sleep(r.delay)
    elif random.random() < 0.3 and len(response) > 15:
        await asyncio.sleep(random.uniform(0.3, 0.8))

    # Send
    try:
        if r.media_type == "sticker" and r.file_id:
            await message.reply_sticker(r.file_id)
        elif r.media_type == "photo" and r.file_id:
            await message.reply_photo(r.file_id, caption=response if response and response != r.file_id else None)
        elif r.media_type == "voice" and r.file_id:
            await message.reply_voice(r.file_id)
        elif r.media_type == "video" and r.file_id:
            await message.reply_video(r.file_id)
        elif r.media_type == "animation" and r.file_id:
            await message.reply_animation(r.file_id)
        else:
            await message.reply_text(response, parse_mode=ParseMode.HTML)
    except (BadRequest, Forbidden) as e:
        logger.warning(f"Brain response failed: {e}")
        try:
            await message.reply_text(response)
        except (BadRequest, Forbidden):
            pass

    logger.debug(f"Brain matched: «{r.trigger}» via {result.method} (score={result.score:.2f})")
    return True


# ═══════════════════════════════════════════════
#  Register
# ═══════════════════════════════════════════════

def register(app) -> None:
    register_mixed(
        app,
        {
            "brain": learn_cmd,
            "learn": learn_cmd,
            "unbrain": unlearn_cmd,
            "unlearn": unlearn_cmd,
            "listbrain": list_cmd,
            "searchbrain": search_cmd,
            "brainstats": stats_cmd,
            "braincats": categories_cmd,
            "clearbrain": clear_cmd,
            "exportbrain": export_cmd,
            "importbrain": import_cmd,
        },
        {
            "یادبگیر": learn_cmd,
            "یاد بگیر": learn_cmd,
            "به من یاد بده": learn_cmd,
            "فراموش کن": unlearn_cmd,
            "لیست脑": list_cmd,
            "لیست یادگیری": list_cmd,
            "جستجو脑": search_cmd,
            "جستجوی یادگیری": search_cmd,
            "آمار脑": stats_cmd,
            "آمار یادگیری": stats_cmd,
            "دسته脑": categories_cmd,
            "پاک脑": clear_cmd,
            "پاک کردن همه": clear_cmd,
            "خروجی脑": export_cmd,
            "ورودی脑": import_cmd,
            "یادگرفته‌ها": list_cmd,
            "چی یاد گرفتی": list_cmd,
        },
    )
