"""Undo system — revert the last admin action.

Each admin action stores a record in bot_data['undo_records']. 
The last record per chat can be undone within 60 seconds.
Usage: /undo
"""
from __future__ import annotations

import logging
import time
from dataclasses import dataclass, field
from typing import Any, Dict, Optional

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, 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__)

UNDO_TTL = 60  # seconds


@dataclass
class UndoRecord:
    action: str
    chat_id: int
    actor_id: int
    target_id: int
    reason: str = ""
    extra: Dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)

    def expired(self) -> bool:
        return time.time() - self.timestamp > UNDO_TTL


def get_records(context: ContextTypes.DEFAULT_TYPE) -> Dict[int, list[UndoRecord]]:
    return context.application.bot_data.setdefault("undo_records", {})


def push_record(context: ContextTypes.DEFAULT_TYPE, rec: UndoRecord) -> None:
    records = get_records(context)
    records.setdefault(rec.chat_id, []).append(rec)
    # Prune expired
    records[rec.chat_id] = [r for r in records[rec.chat_id] if not r.expired()]


def pop_last(context: ContextTypes.DEFAULT_TYPE, chat_id: int) -> Optional[UndoRecord]:
    records = get_records(context)
    lst = records.get(chat_id, [])
    if not lst:
        return None
    rec = lst.pop()
    if rec.expired():
        return None
    return rec


_ACTION_LABELS = {
    "ban": "🔨 بن",
    "unban": "🔓 رفع بن",
    "tban": "⏱ بن موقت",
    "mute": "🔇 سکوت",
    "unmute": "🔊 رفع سکوت",
    "tmute": "⏱ سکوت موقت",
    "kick": "👢 کیک",
    "warn": "⚠️ اخطار",
    "del": "🗑 حذف پیام",
    "pin": "📌 سنجاق",
    "unpin": "📍 حذف سنجاق",
    "promote": "⬆️ ارتقا",
    "demote": "⬇️ عزل",
}


@admin_only
async def undo_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    chat = update.effective_chat
    rec = pop_last(context, chat.id)
    if not rec:
        await update.message.reply_text(
            "ℹ️ عملی برای واگردانی وجود ندارد (یا ۶۰ ثانیه گذشته)."
        )
        return

    label = _ACTION_LABELS.get(rec.action, rec.action)
    try:
        if rec.action == "ban":
            await context.bot.unban_chat_member(rec.chat_id, rec.target_id, only_if_banned=True)
            msg = f"✅ واگردانی بن برای <code>{rec.target_id}</code>"
        elif rec.action == "tban":
            await context.bot.unban_chat_member(rec.chat_id, rec.target_id, only_if_banned=True)
            msg = f"✅ واگردانی بن موقت برای <code>{rec.target_id}</code>"
        elif rec.action == "mute" or rec.action == "tmute":
            await context.bot.restrict_chat_member(
                rec.chat_id, rec.target_id,
                can_send_messages=True, can_send_audios=True, can_send_documents=True,
                can_send_photos=True, can_send_videos=True, can_send_video_notes=True,
                can_send_voice_notes=True, can_send_polls=True, can_send_other_messages=True,
                can_add_web_page_previews=True,
            )
            msg = f"✅ واگردانی سکوت برای <code>{rec.target_id}</code>"
        elif rec.action == "kick":
            await context.bot.unban_chat_member(rec.chat_id, rec.target_id, only_if_banned=False)
            msg = f"✅ واگردانی کیک برای <code>{rec.target_id}</code>"
        elif rec.action == "warn":
            count = await db.get_warns_count(rec.chat_id, rec.target_id)
            if count > 0:
                await db.reset_warns(rec.chat_id, rec.target_id)
            msg = f"✅ اخطار <code>{rec.target_id}</code> پاک شد."
        elif rec.action == "del":
            msg = "❌ واگردانی حذف پیام ممکن نیست."
        elif rec.action == "promote":
            await context.bot.promote_chat_member(rec.chat_id, rec.target_id)
            msg = f"✅ واگردانی ارتقا برای <code>{rec.target_id}</code>"
        elif rec.action == "demote":
            await context.bot.promote_chat_member(
                rec.chat_id, rec.target_id,
                can_change_info=False, can_invite_users=False, can_delete_messages=False,
                can_restrict_members=False, can_pin_messages=False, can_promote_members=False,
            )
            msg = f"✅ واگردانی عزل برای <code>{rec.target_id}</code>"
        else:
            msg = f"❌ واگردانی برای {label} پشتیبانی نمی‌شود."
        await update.message.reply_text(msg, parse_mode=ParseMode.HTML)
    except (BadRequest, Forbidden) as e:
        await update.message.reply_text(f"❌ خطا در واگردانی: {e}")


async def undo_inline_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Handle inline undo buttons."""
    q = update.callback_query
    await q.answer()
    if not q.data or not q.data.startswith("undo_"):
        return
    chat_id = q.message.chat.id
    rec = pop_last(context, chat_id)
    if not rec:
        await q.edit_message_text("ℹ️ مهلت واگردانی تمام شد.", parse_mode=ParseMode.HTML)
        return
    try:
        if rec.action in ("ban", "tban"):
            await context.bot.unban_chat_member(rec.chat_id, rec.target_id, only_if_banned=True)
        elif rec.action in ("mute", "tmute"):
            await context.bot.restrict_chat_member(
                rec.chat_id, rec.target_id,
                can_send_messages=True, can_send_audios=True, can_send_documents=True,
                can_send_photos=True, can_send_videos=True,
            )
        await q.edit_message_text(f"✅ واگردانی شد: {rec.action}", parse_mode=ParseMode.HTML)
    except (BadRequest, Forbidden) as e:
        await q.edit_message_text(f"❌ {e}", parse_mode=ParseMode.HTML)


def register(app) -> None:
    register_mixed(
        app,
        {"undo": undo_cmd},
        {"واگردانی": undo_cmd, "برگشت": undo_cmd},
    )
    app.add_handler(__import__("telegram.ext", fromlist=["CallbackQueryHandler"]).CallbackQueryHandler(
        undo_inline_callback, pattern=r"^undo_"
    ))
