"""Broadcast, backup, and scheduler handlers."""
from __future__ import annotations

import asyncio
import datetime
import io
import json
import logging
import os
import time
from pathlib import Path

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

from bot import db
from bot.config import OWNER_ID
from bot.decorators import admin_only, owner_only, sudo_only
from bot.helpers import is_chat_admin
from bot.infra.db.chat_repository import chat_repository
from bot.persian_router import register_mixed

logger = logging.getLogger(__name__)

# ───────── Broadcast ─────────

@sudo_only
async def broadcast_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message or not update.message.reply_to_message:
        await update.message.reply_text("❌ روی پیامی که می‌خواهید ارسال کنید ریپلای کنید.")
        return
    msg = await update.message.reply_text("🔄 در حال ارسال...")
    chat_ids = await db.get_all_chat_ids()
    if not chat_ids:
        await msg.edit_text("❌ هیچ گروهی یافت نشد.")
        return
    sent = 0
    failed = 0
    for cid in chat_ids:
        try:
            await context.bot.copy_message(
                chat_id=cid,
                from_chat_id=update.effective_chat.id,
                message_id=update.message.reply_to_message.message_id,
            )
            sent += 1
        except (BadRequest, Forbidden):
            failed += 1
        except Exception as e:
            logger.warning(f"Broadcast error to {cid}: {e}")
            failed += 1
        await asyncio.sleep(0.05)
    await msg.edit_text(
        f"✅ پیام به {sent} گروه ارسال شد.\n"
        f"❌ {failed} گروه خطا."
    )

# ───────── Backup ─────────

@owner_only
async def backup_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    chat_id = update.effective_chat.id

    data = {
        "chat_id": chat_id,
        "title": update.effective_chat.title or "Private",
        "exported_at": int(time.time()),
        "settings": {},
        "notes": [],
        "filters": [],
        "custom_commands": [],
        "locks": [],
        "warns": [],
        "learned": [],
    }

    try:
        data.update(await chat_repository.export_chat_data(chat_id))

    except Exception as e:
        await update.message.reply_text(f"⚠️ خطا: {e}")
        return
    ts = int(time.time())
    fname = f"backup_{chat_id}_{ts}.json"
    buf = io.BytesIO(json.dumps(data, ensure_ascii=False, indent=2).encode("utf-8"))
    buf.name = fname
    await update.message.reply_document(
        document=buf,
        caption=f"📦 بکاپ تنظیمات گروه\n{update.effective_chat.title or ''}",
    )

# ───────── Restore ─────────

@owner_only
async def restore_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message.reply_to_message or not update.message.reply_to_message.document:
        await update.message.reply_text("❌ روی فایل بکاپ ریپلای کنید.")
        return
    doc = update.message.reply_to_message.document
    if not doc.file_name or not doc.file_name.endswith(".json"):
        await update.message.reply_text("❌ فقط فایل JSON پشتیبانی می‌شود.")
        return
    status = await update.message.reply_text("⏳ در حال بازگردانی...")
    try:
        f = await doc.get_file()
        bio = io.BytesIO()
        await f.download_to_memory(out=bio)
        bio.seek(0)
        data = json.loads(bio.read().decode("utf-8"))
    except Exception as e:
        await status.edit_text(f"⚠️ خطا در خواندن فایل: {e}")
        return
    if "chat_id" not in data or "settings" not in data:
        await status.edit_text("❌ فرمت فایل نامعتبر است.")
        return
    chat_id = int(data["chat_id"])
    try:
        for k, v in (data.get("settings") or {}).items():
            await db._exec(
                "INSERT OR REPLACE INTO chat_settings (chat_id, key, value) VALUES (?, ?, ?)",
                (chat_id, k, str(v)),
            )
        for note in data.get("notes") or []:
            await db._exec(
                "INSERT OR REPLACE INTO notes (chat_id, name, content, file_id, file_type, buttons) VALUES (?, ?, ?, ?, ?, ?)",
                (chat_id, note.get("name"), note.get("content"), note.get("file_id"), note.get("file_type"), note.get("buttons")),
            )
        for flt in data.get("filters") or []:
            await db._exec(
                "INSERT OR REPLACE INTO filters (chat_id, keyword, response, file_id, file_type) VALUES (?, ?, ?, ?, ?)",
                (chat_id, flt.get("keyword"), flt.get("response"), flt.get("file_id"), flt.get("file_type")),
            )
        for cmd in data.get("custom_commands") or []:
            await db._exec(
                "INSERT OR REPLACE INTO custom_commands (chat_id, command, response) VALUES (?, ?, ?)",
                (chat_id, cmd.get("command"), cmd.get("response")),
            )
        for lock in data.get("locks") or []:
            await db._exec(
                "INSERT OR IGNORE INTO locks (chat_id, lock_type) VALUES (?, ?)",
                (chat_id, lock),
            )
        for warn in data.get("warns") or []:
            await db._exec(
                "INSERT INTO warns (chat_id, user_id, reason, count) VALUES (?, ?, ?, ?)",
                (chat_id, warn.get("user_id"), warn.get("reason"), warn.get("count")),
            )
        for lr in data.get("learned") or []:
            await db._exec(
                "INSERT OR REPLACE INTO learned_responses (chat_id, keyword, response, weight) VALUES (?, ?, ?, ?)",
                (chat_id, lr.get("keyword"), lr.get("response"), lr.get("weight", 1)),
            )
        await status.edit_text(f"✅ بازگردانی انجام شد.\nگروه: {chat_id}")
    except Exception as e:
        logger.exception("restore error: %s", e)
        try:
            await status.edit_text(f"⚠️ خطا: {e}")
        except BadRequest:
            pass

# ───────── Export ─────────

@admin_only
async def export_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    chat_id = update.effective_chat.id

    data = {
        "chat_id": chat_id,
        "title": update.effective_chat.title or "Private",
        "exported_at": int(time.time()),
        "settings": {},
        "notes": [],
        "filters": [],
        "custom_commands": [],
        "locks": [],
        "warns": [],
    }

    try:
        exported = await chat_repository.export_chat_data(chat_id)

        data["settings"] = exported["settings"]
        data["notes"] = exported["notes"]
        data["filters"] = exported["filters"]
        data["custom_commands"] = exported["custom_commands"]
        data["locks"] = exported["locks"]
        data["warns"] = exported["warns"]

    except Exception as e:
        await update.message.reply_text(f"⚠️ خطا: {e}")
        return
    buf = io.BytesIO(json.dumps(data, ensure_ascii=False, indent=2).encode("utf-8"))
    buf.name = f"export_{chat_id}.json"
    await update.message.reply_document(
        document=buf,
        caption=f"📤 خروجی تنظیمات گروه\n{update.effective_chat.title or ''}",
    )

# ───────── Scheduler ─────────

@admin_only
async def schedule_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message or not update.message.text:
        return
    chat_id = update.effective_chat.id
    parts = update.message.text.split(None, 3)
    if len(parts) < 2:
        await update.message.reply_text(
            "📋 دستورات زمان‌بندی:\n\n"
            "/schedule add <ثانیه> <متن> — ارسال یکبار پس از تأخیر\n"
            "/schedule repeat <دقیقه> <متن> — ارسال تکراری هر N دقیقه\n"
            "/schedule list — نمایش لیست\n"
            "/schedule del <id> — حذف زمان‌بندی"
        )
        return
    sub = parts[1].lower()
    if sub == "list":
        rows = await db._fetchall(
            "SELECT id, job_type, interval_secs, text_preview, created_at FROM scheduled_jobs WHERE chat_id=? ORDER BY id",
            (chat_id,),
        )
        if not rows:
            await update.message.reply_text("📭 هیچ زمان‌بندی‌ای ثبت نشده.")
            return
        text = "📋 زمان‌بندی‌ها:\n\n"
        for r in rows:
            icon = "🔁" if r["job_type"] == "repeat" else "⏰"
            val = r["interval_secs"]
            unit = "دقیقه" if r["job_type"] == "repeat" else "ثانیه"
            if r["job_type"] == "repeat":
                val = val // 60
            preview = r["text_preview"][:40]
            text += f"{icon} #{r['id']} — هر {val} {unit} — {preview}\n"
        await update.message.reply_text(text)
        return
    if sub == "del":
        if len(parts) < 3:
            await update.message.reply_text("❌ آیدی را وارد کنید:\n/schedule del <id>")
            return
        try:
            job_id = int(parts[2])
        except ValueError:
            await update.message.reply_text("❌ آیدی باید عدد باشد.")
            return
        row = await db._fetchone(
            "SELECT * FROM scheduled_jobs WHERE id=? AND chat_id=?",
            (job_id, chat_id),
        )
        if not row:
            await update.message.reply_text("❌ زمان‌بندی یافت نشد.")
            return
        for job in context.job_queue.get_jobs_by_name(f"sched_{job_id}"):
            job.schedule_removal()
        await db._exec("DELETE FROM scheduled_jobs WHERE id=?", (job_id,))
        await update.message.reply_text(f"✅ زمان‌بندی #{job_id} حذف شد.")
        return
    if sub in ("add", "repeat"):
        if len(parts) < 4:
            await update.message.reply_text(f"❌ Usage:\n/schedule {sub} <زمان> <متن>")
            return
        try:
            val = int(parts[2])
        except ValueError:
            await update.message.reply_text("❌ زمان باید عدد باشد.")
            return
        if val < 5:
            await update.message.reply_text("❌ حداقل ۵ ثانیه / دقیقه.")
            return
        text = parts[3]
        job_type = "repeat" if sub == "repeat" else "once"
        interval = val * 60 if sub == "repeat" else val
        cur = await db._exec(
            "INSERT INTO scheduled_jobs (chat_id, job_type, interval_secs, text_preview, created_by) VALUES (?, ?, ?, ?, ?)",
            (chat_id, job_type, interval, text[:200], update.effective_user.id),
        )
        job_id = cur.lastrowid
        if sub == "repeat":
            context.job_queue.run_repeating(
                _send_scheduled,
                interval=interval,
                data={"job_id": job_id, "chat_id": chat_id, "text": text},
                name=f"sched_{job_id}",
            )
        else:
            context.job_queue.run_once(
                _send_scheduled_once,
                when=interval,
                data={"job_id": job_id, "chat_id": chat_id, "text": text},
                name=f"sched_{job_id}",
            )
        unit = "دقیقه" if sub == "repeat" else "ثانیه"
        await update.message.reply_text(
            f"✅ زمان‌بندی ثبت شد!\n"
            f"شناسه: #{job_id}\n"
            f"نوع: {'تکراری' if sub == 'repeat' else 'یکبار'}\n"
            f"زمان: هر {val} {unit}"
        )
        return
    await update.message.reply_text("❌ دستور نامعتبر.")

async def _send_scheduled(context: ContextTypes.DEFAULT_TYPE) -> None:
    data = context.job.data
    try:
        await context.bot.send_message(chat_id=data["chat_id"], text=data["text"])
    except Exception as e:
        logger.warning(f"Scheduled job {data.get('job_id')} failed: {e}")

async def _send_scheduled_once(context: ContextTypes.DEFAULT_TYPE) -> None:
    data = context.job.data
    try:
        await context.bot.send_message(chat_id=data["chat_id"], text=data["text"])
    except Exception as e:
        logger.warning(f"Scheduled once job {data.get('job_id')} failed: {e}")
    finally:
        await db._exec("DELETE FROM scheduled_jobs WHERE id=?", (data["job_id"],))

# ───────── Autopost ─────────

@admin_only
async def autopost_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message or not update.message.text:
        return
    chat_id = update.effective_chat.id
    channel = " ".join(context.args).strip() if context.args else ""
    if not channel:
        current = await db.get_setting(chat_id, "autopost_channel", "")
        status = f"🟢 {current}" if current else "🔴 غیرفعال"
        await update.message.reply_text(
            f"📡 <b>ارسال خودکار آمار</b>\n\n"
            f"وضعیت: {status}\n\n"
            f"Usage:\n"
            f"/autopost @channel — فعال‌سازی\n"
            f"/autopost off — غیرفعال",
            parse_mode=ParseMode.HTML,
        )
        return
    if channel.lower() == "off":
        await db.set_setting(chat_id, "autopost_channel", "")
        for job in context.job_queue.get_jobs_by_name(f"autopost_{chat_id}"):
            job.schedule_removal()
        await update.message.reply_text("✅ ارسال خودکار آمار غیرفعال شد.")
        return
    await db.set_setting(chat_id, "autopost_channel", channel)
    context.job_queue.run_repeating(
        _auto_stats_job,
        interval=86400,
        data={"chat_id": chat_id, "channel": channel},
        name=f"autopost_{chat_id}",
    )
    await update.message.reply_text(f"✅ ارسال خودکار آمار به {channel} فعال شد (هر ۲۴ ساعت).")

async def _auto_stats_job(context: ContextTypes.DEFAULT_TYPE) -> None:
    data = context.job.data
    chat_id = data["chat_id"]
    channel = data["channel"]
    try:
        user_count = await db.get_chat_user_count(chat_id)
        msg_count = await db.get_chat_total_msgs(chat_id)
        top = await db.get_top_users(chat_id, 5)
        top_text = ""
        for i, u in enumerate(top, 1):
            uname = f"`{u['user_id']}`"
            try:
                member = await context.bot.get_chat_member(chat_id, u["user_id"])
                uname = member.user.mention_html() if member.user else uname
            except Exception:
                pass
            top_text += f"{i}. {uname} — {u['msg_count']} پیام\n"
        now = datetime.datetime.now()
        text = (
            f"📊 گزارش روزانه — {now.strftime('%Y/%m/%d')}\n\n"
            f"👥 تعداد کاربران: {user_count}\n"
            f"💬 تعداد پیام‌ها: {msg_count}\n\n"
            f"🏆 کاربران برتر:\n{top_text}"
        )
        await context.bot.send_message(chat_id=channel, text=text, parse_mode=ParseMode.HTML)
    except Exception as e:
        logger.warning(f"Auto-post failed for {channel}: {e}")

# ───────── Restore scheduled jobs on startup ─────────

async def restore_scheduled_jobs(app) -> None:
    try:
        rows = await db._fetchall("SELECT * FROM scheduled_jobs")
        for r in rows:
            job_id, chat_id, job_type, interval, text = r["id"], r["chat_id"], r["job_type"], r["interval_secs"], r["text_preview"]
            if job_type == "repeat":
                app.job_queue.run_repeating(
                    _send_scheduled,
                    interval=interval,
                    data={"job_id": job_id, "chat_id": chat_id, "text": text},
                    name=f"sched_{job_id}",
                )
            else:
                remaining = max(1, interval - (int(time.time()) - r["created_at"]))
                app.job_queue.run_once(
                    _send_scheduled_once,
                    when=remaining,
                    data={"job_id": job_id, "chat_id": chat_id, "text": text},
                    name=f"sched_{job_id}",
                )
    except Exception as e:
        logger.warning(f"Could not restore scheduled jobs: {e}")

# ───────── Health & maintenance jobs ─────────

def register_jobs(app) -> None:
    from bot.db import optimize_db
    async def _health_job(context):
        await optimize_db()
    app.job_queue.run_repeating(_health_job, interval=21600, first=3600)

    async def _forget_job(context):
        removed = await db.smart_forget(days=30)
        if removed:
            logger.info(f"Smart forget: removed {removed} unused responses")
    app.job_queue.run_daily(_forget_job, time=datetime.time(hour=3, minute=0))

# ───────── Registration ─────────

def register(app) -> None:
    register_mixed(app, {
        "broadcast": broadcast_cmd,
        "backup": backup_cmd,
        "restore": restore_cmd,
        "export": export_cmd,
        "schedule": schedule_cmd,
        "autopost": autopost_cmd,
    }, {
        "بکاپ": backup_cmd,
        "پشتیبان‌گیری": backup_cmd,
        "بازگردانی": restore_cmd,
        "خروجی": export_cmd,
        "خروجی گروه": export_cmd,
        "زمان‌بندی": schedule_cmd,
        "برنامه": schedule_cmd,
        "ارسال خودکار": autopost_cmd,
        "پست خودکار": autopost_cmd,
    })
