"""منطق اصلی ربات (مستقل از کتابخانه روبیکا).

فقط از طریق شیء `plat` با روبیکا حرف می‌زند؛ متدهای لازم:
  await plat.send(chat, text, reply_to=None)
  await plat.delete(chat, msg_id)
  await plat.ban(chat, uid)
  await plat.is_admin(chat, uid) -> bool
  await plat.members(chat) -> list[(uid, username, name)]
  await plat.is_joined(channel_guid, uid) -> bool
"""
import re
import time
from collections import defaultdict, deque

import config
from db import DB
from games import START_WORDS, GameManager

LINK_RE = re.compile(r"(https?://|www\.|rubika\.ir|t\.me/|@[A-Za-z0-9_]{4,})", re.I)

HELP = """📖 راهنمای ربات
🎮 بازی‌ها
• شروع تاس | شروع سنگ کاغذ قیچی | شروع پنالتی
• لغو  (لغو بازی فعال)
💰 سکه
• سکه  | روزانه | انتقال 100 (ریپلای) | برترین‌ها | تاریخچه | آمار
🛡 مدیریت (ادمین‌ها، با ریپلای روی پیام کاربر)
• اخطار [دلیل] | حذف اخطار | سکوت [دقیقه] | رفع سکوت | بن | رفع بن
• شارژ 100 (ریپلای؛ هدیه سکه به کاربر)
• تگ همه [متن]
• قفل لینک | باز لینک | قفل ضداسپم | باز ضداسپم
• کلمه ممنوع X | حذف کلمه X | لیست کلمات
• جوین اجباری GUID_کانال | جوین اجباری خاموش"""


def norm(t: str) -> str:
    t = (t or "").replace("ي", "ی").replace("ك", "ک").replace("\u200c", " ")
    t = t.translate(str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "0123456789" * 2))
    return re.sub(r"\s+", " ", t).strip()


class Core:
    def __init__(self, plat, db: DB | None = None):
        self.plat = plat
        self.db = db or DB()
        self.games = GameManager(self.db)
        self.flood: dict[tuple, deque] = defaultdict(lambda: deque(maxlen=20))
        self.admin_cache: dict[tuple, tuple[float, bool]] = {}
        self.join_notice: dict[tuple, float] = {}

    # ------------------------------------------------------------------ کمکی
    async def say(self, chat, text, reply_to=None):
        await self.plat.send(chat, text, reply_to)

    async def admin(self, chat, uid) -> bool:
        if uid in config.OWNERS:
            return True
        k = (chat, uid)
        c = self.admin_cache.get(k)
        if c and time.time() - c[0] < 60:
            return c[1]
        ok = await self.plat.is_admin(chat, uid)
        self.admin_cache[k] = (time.time(), ok)
        return ok

    async def warn(self, chat, uid, reason, msg_id=None):
        n = self.db.add_warn(chat, uid)
        name = self.db.name(uid)
        text = f"⚠️ اخطار به {name}\n📝 دلیل: {reason}\n📊 اخطارها: {n}/{config.MAX_WARNS}"
        if n >= config.MAX_WARNS:
            await self.plat.ban(chat, uid)
            self.db.set_ban(chat, uid, True)
            self.db.reset_warns(chat, uid)
            text += f"\n🚫 به دلیل رسیدن به {config.MAX_WARNS} اخطار، {name} بن شد."
        await self.say(chat, text, msg_id)

    # ------------------------------------------------------------------ ورودی اصلی
    async def handle(self, chat, uid, name, text, msg_id, reply_uid=None):
        raw = text or ""
        t = norm(raw)
        if not t:
            return
        self.db.touch_user(uid, name)
        is_admin = await self.admin(chat, uid)

        m = self.db.member(chat, uid)
        if m["banned"] and not is_admin:
            await self.plat.delete(chat, msg_id)
            await self.plat.ban(chat, uid)
            return
        if m["muted_until"] > time.time() and not is_admin:     # سکوت = حذف پیام‌ها
            await self.plat.delete(chat, msg_id)
            return

        if not is_admin:
            if await self._forced_join(chat, uid, name, msg_id):
                return
            if await self._filters(chat, uid, raw, t, msg_id):
                return

        # بازی‌ها (قبل از دستورات، برای گرفتن انتخاب‌ها)
        if t == "لغو":
            for msg in self.games.cancel(chat, uid, is_admin):
                await self.say(chat, msg, msg_id)
            return
        if t in START_WORDS:
            for msg in self.games.start(chat, uid, START_WORDS[t]):
                await self.say(chat, msg, msg_id)
            return
        handled, msgs, delete = self.games.on_text(chat, uid, t)
        if delete:
            await self.plat.delete(chat, msg_id)
        if handled:
            for msg in msgs:
                await self.say(chat, msg)
            return

        await self._commands(chat, uid, name, t, msg_id, reply_uid, is_admin)

    # ------------------------------------------------------------------ فیلترها
    async def _forced_join(self, chat, uid, name, msg_id) -> bool:
        ch = self.db.get_setting(chat, "force_channel")
        if not ch:
            return False
        if await self.plat.is_joined(ch, uid):
            return False
        await self.plat.delete(chat, msg_id)
        k = (chat, uid)
        if time.time() - self.join_notice.get(k, 0) > 60:
            self.join_notice[k] = time.time()
            link = self.db.get_setting(chat, "force_link", "")
            await self.say(chat, f"🔒 {name} عزیز، برای چت در گروه ابتدا در کانال عضو شو.\n{link}")
        return True

    async def _filters(self, chat, uid, raw, t, msg_id) -> bool:
        # ضداسپم
        if self.db.get_setting(chat, "antispam", "1") == "1":
            q = self.flood[(chat, uid)]
            now = time.time()
            q.append(now)
            recent = [x for x in q if now - x <= config.FLOOD_SECONDS]
            if len(recent) > config.FLOOD_MESSAGES:
                self.db.set_mute(chat, uid, now + config.FLOOD_MUTE_MINUTES * 60)
                q.clear()
                await self.plat.delete(chat, msg_id)
                await self.say(chat, f"🛡 {self.db.name(uid)} به دلیل اسپم {config.FLOOD_MUTE_MINUTES} دقیقه سکوت شد.")
                return True
        # لینک
        if self.db.get_setting(chat, "lock_link", "0") == "1" and LINK_RE.search(raw):
            await self.plat.delete(chat, msg_id)
            await self.warn(chat, uid, "ارسال لینک ممنوع است")
            return True
        # کلمات ممنوع
        for w in self.db.badwords(chat):
            if w in t:
                await self.plat.delete(chat, msg_id)
                await self.warn(chat, uid, "استفاده از کلمه ممنوعه")
                return True
        return False

    # ------------------------------------------------------------------ دستورات
    async def _commands(self, chat, uid, name, t, msg_id, reply_uid, is_admin):
        db = self.db
        if t in ("راهنما", "help", "/help", "/start"):
            return await self.say(chat, HELP, msg_id)
        if t in ("سکه", "موجودی"):
            return await self.say(chat, f"💳 موجودی {name}: {db.coins(uid)} سکه", msg_id)
        if t == "روزانه":
            wait = db.claim_daily(uid)
            if wait:
                return await self.say(chat, f"⏳ جایزه روزانه را گرفته‌اید. {wait // 3600} ساعت و {wait % 3600 // 60} دقیقه دیگر.", msg_id)
            return await self.say(chat, f"🎁 {config.DAILY_COINS} سکه به حساب شما اضافه شد.", msg_id)
        if t == "برترین ها" or t == "برترین‌ها":
            rows = db.top()
            lines = [f"{i + 1}. {r['name']} — {r['coins']} سکه" for i, r in enumerate(rows)]
            return await self.say(chat, "🏅 برترین‌ها\n" + "\n".join(lines), msg_id)
        if t == "آمار":
            s = db.stats(uid)
            return await self.say(chat, f"📊 {name}\n🏆 برد: {s['wins']} | ❌ باخت: {s['losses']} | 💳 {s['coins']} سکه", msg_id)
        if t == "تاریخچه":
            rows = db.history(chat)
            if not rows:
                return await self.say(chat, "هنوز بازی‌ای ثبت نشده.", msg_id)
            lines = [f"• {r['game']} | برنده: {db.name(r['winner'])} | {r['prize']} سکه" for r in rows]
            return await self.say(chat, "🗂 تاریخچه آخرین بازی‌ها\n" + "\n".join(lines), msg_id)

        m = re.match(r"^انتقال (\d+)$", t)
        if m:
            if not reply_uid or reply_uid == uid:
                return await self.say(chat, "روی پیام کاربر مقصد ریپلای کن.", msg_id)
            amount = int(m.group(1))
            if db.transfer(uid, reply_uid, amount):
                return await self.say(chat, f"✅ {amount} سکه به {db.name(reply_uid)} منتقل شد.", msg_id)
            return await self.say(chat, "❌ موجودی کافی نیست یا مبلغ نامعتبر است.", msg_id)

        if not is_admin:
            return

        # ---------- فقط ادمین ----------
        if t.startswith("اخطار") and reply_uid:
            reason = t[5:].strip() or "رعایت نکردن قوانین"
            return await self.warn(chat, reply_uid, reason, msg_id)
        if t == "حذف اخطار" and reply_uid:
            n = db.remove_warn(chat, reply_uid)
            return await self.say(chat, f"✅ یک اخطار {db.name(reply_uid)} حذف شد ({n}/{config.MAX_WARNS}).", msg_id)
        m = re.match(r"^سکوت ?(\d+)?$", t)
        if m and reply_uid:
            mins = int(m.group(1) or config.DEFAULT_MUTE_MINUTES)
            db.set_mute(chat, reply_uid, time.time() + mins * 60)
            return await self.say(chat, f"🔇 {db.name(reply_uid)} به مدت {mins} دقیقه سکوت شد.", msg_id)
        if t == "رفع سکوت" and reply_uid:
            db.set_mute(chat, reply_uid, 0)
            return await self.say(chat, f"🔊 سکوت {db.name(reply_uid)} برداشته شد.", msg_id)
        if t == "بن" and reply_uid:
            await self.plat.ban(chat, reply_uid)
            db.set_ban(chat, reply_uid, True)
            return await self.say(chat, f"🚫 {db.name(reply_uid)} بن شد.", msg_id)
        if t == "رفع بن" and reply_uid:
            db.set_ban(chat, reply_uid, False)
            return await self.say(chat, f"✅ بن {db.name(reply_uid)} برداشته شد (باید دوباره با لینک وارد شود).", msg_id)
        m = re.match(r"^شارژ (\d+)$", t)
        if m and reply_uid:
            db.add_coins(reply_uid, int(m.group(1)))
            return await self.say(chat, f"💰 {m.group(1)} سکه به {db.name(reply_uid)} اضافه شد.", msg_id)

        if t.startswith("تگ همه"):
            extra = t[6:].strip()
            members = await self.plat.members(chat)
            tags = [f"@{u}" if u else n for (_, u, n) in members]
            for i in range(0, len(tags), 20):
                head = (extra + "\n") if i == 0 and extra else "📢 \n"
                await self.say(chat, head + " ".join(tags[i:i + 20]))
            return

        toggles = {"قفل لینک": ("lock_link", "1"), "باز لینک": ("lock_link", "0"),
                   "قفل ضداسپم": ("antispam", "1"), "باز ضداسپم": ("antispam", "0")}
        if t in toggles:
            db.set_setting(chat, *toggles[t])
            return await self.say(chat, "✅ انجام شد.", msg_id)
        if t.startswith("کلمه ممنوع "):
            db.add_badword(chat, t[11:].strip())
            return await self.say(chat, "✅ به لیست کلمات ممنوع اضافه شد.", msg_id)
        if t.startswith("حذف کلمه "):
            db.del_badword(chat, t[9:].strip())
            return await self.say(chat, "✅ از لیست حذف شد.", msg_id)
        if t == "لیست کلمات":
            return await self.say(chat, "🚫 کلمات ممنوع:\n" + ("\n".join(db.badwords(chat)) or "—"), msg_id)
        if t == "جوین اجباری خاموش":
            db.set_setting(chat, "force_channel", "")
            return await self.say(chat, "✅ جوین اجباری خاموش شد.", msg_id)
        if t.startswith("جوین اجباری "):
            parts = t[12:].split()
            db.set_setting(chat, "force_channel", parts[0])
            db.set_setting(chat, "force_link", parts[1] if len(parts) > 1 else "")
            return await self.say(chat, "✅ جوین اجباری فعال شد.", msg_id)

    # ------------------------------------------------------------------ زمان‌سنج بازی‌ها
    async def tick(self):
        for chat, msg in self.games.tick():
            await self.say(chat, msg)
