"""Admin command panel: stats, products, users, deposits, announcements."""
from telegram import Update
from telegram.ext import (ContextTypes, ConversationHandler, CommandHandler,
                          MessageHandler, CallbackQueryHandler, filters)
from database.db import DB
from utils.decorators import admin_only
from utils.helpers import money
from utils.keyboards import inline
import asyncio, logging
log = logging.getLogger("genboost.admin")

# ---------- Auto-broadcast helper ----------
async def _broadcast_product(context, pid: int, header: str):
    """Send a nicely formatted product card to every active user."""
    p = await DB.fetchone(
        "SELECT p.id, p.name, p.description, p.price, p.stock, p.is_active, "
        "c.name AS cat FROM products p LEFT JOIN categories c ON c.id=p.category_id "
        "WHERE p.id=?", (pid,))
    if not p:
        return
    stock = p["stock"]
    status = ("✅ <b>In Stock</b>" if (p["is_active"] and stock > 0)
              else "🚫 <b>Out of Stock</b>")
    text = (
        f"{header}\n\n"
        f"🛍 <b>{p['name']}</b>\n"
        f"📂 Category: <i>{p['cat'] or '—'}</i>\n"
        f"💵 Price: <b>{money(p['price'])}</b>\n"
        f"📦 Stock: <b>{stock}</b>\n"
        f"{status}\n\n"
        f"{(p['description'] or '').strip()}\n\n"
        "👉 Open the bot and tap <b>🛍 Products</b> to buy!"
    )
    rows = await DB.fetchall("SELECT user_id FROM users WHERE is_banned=0")
    sent = delivered = failed = 0
    for r in rows:
        sent += 1
        try:
            await context.bot.send_message(r["user_id"], text, parse_mode="HTML",
                                           disable_web_page_preview=True)
            delivered += 1
        except Exception:
            failed += 1
        if sent % 25 == 0:
            await asyncio.sleep(1)
    try:
        await DB.execute(
            "INSERT INTO broadcasts(admin_id, payload, sent, delivered, failed) VALUES (?,?,?,?,?)",
            (0, f"[auto] product#{pid}", sent, delivered, failed))
    except Exception:
        pass
    return delivered, failed

# ---------- /admin overview ----------
@admin_only
async def admin_panel(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text = (
        "⚙ <b>Admin Panel</b>\n\n"
        "Commands:\n"
        "/stats — statistics\n"
        "/users [query] — list/search users\n"
        "/setbal &lt;user_id&gt; &lt;amount&gt; — set wallet balance\n"
        "/ban &lt;user_id&gt; · /unban &lt;user_id&gt;\n\n"
        "<b>Catalog</b>\n"
        "/addcat &lt;name&gt; · /cats\n"
        "/rencat &lt;id&gt; &lt;new name&gt; · /delcat &lt;id&gt;\n"
        "/addproduct — guided product wizard\n"
        "/products · /delproduct &lt;id&gt; · /toggleproduct &lt;id&gt;\n"
        "/setstock &lt;id&gt; &lt;n&gt; · /setprice &lt;id&gt; &lt;amt&gt;\n"
        "/rename &lt;id&gt; &lt;new name&gt; · /editdesc &lt;id&gt; &lt;text&gt;\n\n"
        "🆔 /ids — list all category &amp; product IDs (for editing)\n"
        "💎 <b>Premium emoji</b> anywhere in a name/description: "
        "<code>{pe:EMOJI_ID}🔥 Name</code> — paste the custom emoji id and the next emoji becomes premium.\n"
        "📢 New products &amp; stock updates auto-broadcast to all users.\n\n"
        "<b>Deposits</b>\n"
        "/pending — pending deposits\n"
        "/approve &lt;id&gt; · /reject &lt;id&gt; [reason]\n\n"
        "<b>Support</b>\n"
        "/tickets — open tickets · /reply &lt;id&gt; &lt;text&gt;\n\n"
        "<b>Announcements & Broadcast</b>\n"
        "/announce &lt;title&gt; | &lt;body&gt;\n"
        "/broadcast &lt;text&gt; — send to all users"
    )
    await update.message.reply_html(text)

# ---------- Stats ----------
@admin_only
async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE):
    async def n(sql, p=()):
        return (await DB.fetchone(sql, p))[0]
    total_users = await n("SELECT COUNT(*) FROM users")
    active_users = await n("SELECT COUNT(*) FROM users WHERE is_active=1")
    new_today = await n("SELECT COUNT(*) FROM users WHERE created_at >= date('now')")
    total_dep = await n("SELECT COALESCE(SUM(amount),0) FROM deposits WHERE status='approved'")
    pending_dep = await n("SELECT COUNT(*) FROM deposits WHERE status='pending'")
    total_rev = await n("SELECT COALESCE(SUM(amount),0) FROM orders")
    total_orders = await n("SELECT COUNT(*) FROM orders")
    ref_paid = await n("SELECT COALESCE(SUM(amount),0) FROM referral_rewards")
    top = await DB.fetchall(
        "SELECT product_name, COUNT(*) c FROM orders GROUP BY product_name "
        "ORDER BY c DESC LIMIT 5")
    text = (
        "📊 <b>Statistics</b>\n\n"
        f"👥 Total Users: <b>{total_users}</b> (active: {active_users}, new today: {new_today})\n"
        f"💰 Total Deposits: <b>{money(total_dep)}</b> (pending: {pending_dep})\n"
        f"🛒 Orders: <b>{total_orders}</b> · Revenue: <b>{money(total_rev)}</b>\n"
        f"🎁 Referral Rewards Paid: <b>{money(ref_paid)}</b>\n\n"
        "🏆 <b>Top Products</b>:\n" +
        ("\n".join(f"• {t['product_name']} — {t['c']}" for t in top) or "—")
    )
    await update.message.reply_html(text)

# ---------- Users ----------
@admin_only
async def users_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    q = " ".join(context.args).strip()
    if q:
        like = f"%{q}%"
        rows = await DB.fetchall(
            "SELECT user_id, username, first_name, balance FROM users "
            "WHERE CAST(user_id AS TEXT) LIKE ? OR username LIKE ? OR first_name LIKE ? "
            "ORDER BY created_at DESC LIMIT 20", (like, like, like))
    else:
        rows = await DB.fetchall(
            "SELECT user_id, username, first_name, balance FROM users "
            "ORDER BY created_at DESC LIMIT 20")
    if not rows:
        await update.message.reply_text("No users found."); return
    text = "<b>Users</b>\n\n" + "\n".join(
        f"<code>{r['user_id']}</code> @{r['username'] or '—'} {r['first_name'] or ''} — {money(r['balance'])}"
        for r in rows)
    await update.message.reply_html(text)

@admin_only
async def setbal(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if len(context.args) != 2:
        await update.message.reply_text("Usage: /setbal <user_id> <amount>"); return
    uid, amt = int(context.args[0]), float(context.args[1])
    await DB.execute("UPDATE users SET balance=? WHERE user_id=?", (amt, uid))
    await update.message.reply_text(f"✅ Balance for {uid} set to {money(amt)}")

@admin_only
async def ban(update: Update, context: ContextTypes.DEFAULT_TYPE):
    uid = int(context.args[0])
    await DB.execute("UPDATE users SET is_banned=1 WHERE user_id=?", (uid,))
    await update.message.reply_text(f"⛔ User {uid} banned.")

@admin_only
async def unban(update: Update, context: ContextTypes.DEFAULT_TYPE):
    uid = int(context.args[0])
    await DB.execute("UPDATE users SET is_banned=0 WHERE user_id=?", (uid,))
    await update.message.reply_text(f"✅ User {uid} unbanned.")

# ---------- Categories ----------
@admin_only
async def addcat(update: Update, context: ContextTypes.DEFAULT_TYPE):
    name = " ".join(context.args).strip()
    if not name:
        await update.message.reply_text("Usage: /addcat <name>"); return
    try:
        cur = await DB.execute("INSERT INTO categories(name) VALUES (?)", (name,))
        await update.message.reply_text(f"✅ Category #{cur.lastrowid} added: {name}")
    except Exception:
        await update.message.reply_text("Category already exists.")

@admin_only
async def cats_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    rows = await DB.fetchall("SELECT id, name FROM categories ORDER BY id")
    if not rows:
        await update.message.reply_text("No categories yet."); return
    await update.message.reply_text(
        "\n".join(f"#{r['id']} — {r['name']}" for r in rows))

# ---------- Product wizard ----------
P_CAT, P_NAME, P_DESC, P_PRICE, P_STOCK, P_TYPE, P_CONTENT, P_FILE = range(8)

@admin_only
async def addproduct(update, context):
    rows = await DB.fetchall("SELECT id, name FROM categories ORDER BY name")
    if not rows:
        await update.message.reply_text("Create a category first with /addcat <name>.")
        return ConversationHandler.END
    kb = inline([[(r["name"], f"padd:cat:{r['id']}")] for r in rows])
    await update.message.reply_text("Choose a category:", reply_markup=kb)
    return P_CAT

async def p_cat(update, context):
    q = update.callback_query; await q.answer()
    context.user_data["p"] = {"category_id": int(q.data.split(":")[2])}
    await q.edit_message_text("Send product <b>name</b>:", parse_mode="HTML")
    return P_NAME

async def p_name(update, context):
    context.user_data["p"]["name"] = update.message.text.strip()
    await update.message.reply_text("Send <b>description</b> (or - to skip):", parse_mode="HTML")
    return P_DESC

async def p_desc(update, context):
    t = update.message.text.strip()
    context.user_data["p"]["description"] = "" if t == "-" else t
    await update.message.reply_text("Send <b>price in USD</b> (e.g. 4.99):", parse_mode="HTML")
    return P_PRICE

async def p_price(update, context):
    try: context.user_data["p"]["price"] = float(update.message.text)
    except ValueError:
        await update.message.reply_text("Invalid number, try again."); return P_PRICE
    await update.message.reply_text("Send <b>stock quantity</b> (integer):", parse_mode="HTML")
    return P_STOCK

async def p_stock(update, context):
    try: context.user_data["p"]["stock"] = int(update.message.text)
    except ValueError:
        await update.message.reply_text("Invalid integer, try again."); return P_STOCK
    kb = inline([[(t, f"padd:type:{t}")] for t in
                 ["text", "credentials", "license", "link", "file"]])
    await update.message.reply_text("Choose <b>delivery type</b>:", parse_mode="HTML", reply_markup=kb)
    return P_TYPE

async def p_type(update, context):
    q = update.callback_query; await q.answer()
    context.user_data["p"]["product_type"] = q.data.split(":")[2]
    await q.edit_message_text(
        "Send the <b>delivery content</b> (text/link/credentials/license key).\n"
        "If your product is a file, send any short note here — you'll upload the file next.",
        parse_mode="HTML")
    return P_CONTENT

async def p_content(update, context):
    context.user_data["p"]["delivery_content"] = update.message.text
    if context.user_data["p"]["product_type"] == "file":
        await update.message.reply_text("Now upload the <b>file</b> (document):", parse_mode="HTML")
        return P_FILE
    return await _save_product(update, context)

async def p_file(update, context):
    if not update.message.document:
        await update.message.reply_text("Please send a document file."); return P_FILE
    context.user_data["p"]["delivery_file_id"] = update.message.document.file_id
    return await _save_product(update, context)

async def _save_product(update, context):
    p = context.user_data.pop("p")
    cur = await DB.execute(
        "INSERT INTO products(category_id, name, description, price, stock, "
        "product_type, delivery_content, delivery_file_id) VALUES (?,?,?,?,?,?,?,?)",
        (p["category_id"], p["name"], p.get("description", ""), p["price"], p["stock"],
         p["product_type"], p.get("delivery_content", ""), p.get("delivery_file_id")))
    pid = cur.lastrowid
    await update.message.reply_text(f"✅ Product #{pid} created. Broadcasting to users…")
    try:
        res = await _broadcast_product(context, pid, "🆕 <b>New Product Available!</b>")
        if res:
            d, f = res
            await update.message.reply_text(f"📢 Broadcast sent. Delivered: {d} · Failed: {f}")
    except Exception as e:
        log.exception("broadcast failed: %s", e)
    return ConversationHandler.END

async def p_cancel(update, context):
    context.user_data.pop("p", None)
    if update.message: await update.message.reply_text("Cancelled.")
    return ConversationHandler.END

def product_wizard():
    return ConversationHandler(
        entry_points=[CommandHandler("addproduct", addproduct)],
        states={
            P_CAT:     [CallbackQueryHandler(p_cat, pattern=r"^padd:cat:\d+$")],
            P_NAME:    [MessageHandler(filters.TEXT & ~filters.COMMAND, p_name)],
            P_DESC:    [MessageHandler(filters.TEXT & ~filters.COMMAND, p_desc)],
            P_PRICE:   [MessageHandler(filters.TEXT & ~filters.COMMAND, p_price)],
            P_STOCK:   [MessageHandler(filters.TEXT & ~filters.COMMAND, p_stock)],
            P_TYPE:    [CallbackQueryHandler(p_type, pattern=r"^padd:type:")],
            P_CONTENT: [MessageHandler(filters.TEXT & ~filters.COMMAND, p_content)],
            P_FILE:    [MessageHandler(filters.Document.ALL, p_file)],
        },
        fallbacks=[CommandHandler("cancel", p_cancel)],
        allow_reentry=True, per_message=False,
    )

@admin_only
async def products_cmd(update, context):
    rows = await DB.fetchall(
        "SELECT id, name, price, stock, is_active FROM products ORDER BY id DESC LIMIT 50")
    if not rows:
        await update.message.reply_text("No products yet."); return
    await update.message.reply_text("\n".join(
        f"#{r['id']} {'✅' if r['is_active'] else '🚫'} {r['name']} — {money(r['price'])} ({r['stock']} left)"
        for r in rows))

@admin_only
async def delproduct(update, context):
    pid = int(context.args[0])
    await DB.execute("DELETE FROM products WHERE id=?", (pid,))
    await update.message.reply_text(f"🗑 Product #{pid} deleted.")

@admin_only
async def toggleproduct(update, context):
    pid = int(context.args[0])
    await DB.execute("UPDATE products SET is_active = 1 - is_active WHERE id=?", (pid,))
    await update.message.reply_text(f"🔁 Toggled product #{pid}.")

@admin_only
async def setstock(update, context):
    pid, n = int(context.args[0]), int(context.args[1])
    await DB.execute("UPDATE products SET stock=? WHERE id=?", (n, pid))
    await update.message.reply_text(f"✅ Product #{pid} stock = {n}. Broadcasting update…")
    header = ("📦 <b>Stock Restocked!</b>" if n > 0
              else "🚫 <b>Out of Stock</b>")
    try:
        res = await _broadcast_product(context, pid, header)
        if res:
            d, f = res
            await update.message.reply_text(f"📢 Broadcast sent. Delivered: {d} · Failed: {f}")
    except Exception as e:
        log.exception("stock broadcast failed: %s", e)

# ---------- /ids — list all category & product IDs ----------
@admin_only
async def ids_cmd(update, context):
    cats = await DB.fetchall("SELECT id, name FROM categories ORDER BY id")
    prods = await DB.fetchall(
        "SELECT p.id, p.name, p.price, p.stock, p.is_active, c.name AS cat "
        "FROM products p LEFT JOIN categories c ON c.id=p.category_id "
        "ORDER BY p.category_id, p.id")
    parts = ["🆔 <b>All IDs — Categories & Products</b>\n"]
    parts.append("\n📂 <b>Categories</b>")
    if cats:
        for c in cats:
            parts.append(f"  <code>#{c['id']}</code> — {c['name']}")
    else:
        parts.append("  —")
    parts.append("\n🛍 <b>Products</b>")
    if prods:
        cur_cat = None
        for p in prods:
            if p["cat"] != cur_cat:
                cur_cat = p["cat"]
                parts.append(f"\n<i>{cur_cat or 'Uncategorized'}</i>")
            mark = "✅" if p["is_active"] else "🚫"
            parts.append(
                f"  <code>#{p['id']}</code> {mark} {p['name']} — "
                f"{money(p['price'])} · stock {p['stock']}")
    else:
        parts.append("  —")
    parts.append(
        "\n\n<b>Edit commands:</b>\n"
        "/rename &lt;id&gt; · /setprice &lt;id&gt; · /setstock &lt;id&gt;\n"
        "/editdesc &lt;id&gt; · /editcontent &lt;id&gt; · /toggleproduct &lt;id&gt;\n"
        "/delproduct &lt;id&gt; · /rencat &lt;id&gt; · /delcat &lt;id&gt;")
    # Telegram 4096 char limit safety — chunk by lines so we never split a
    # <tg-emoji> tag or a {pe:ID}emoji marker in half.
    chunk, size = [], 0
    LIMIT = 3500
    for line in parts:
        ln = len(line) + 1
        if size + ln > LIMIT and chunk:
            await update.message.reply_html("\n".join(chunk), disable_web_page_preview=True)
            chunk, size = [], 0
        chunk.append(line)
        size += ln
    if chunk:
        await update.message.reply_html("\n".join(chunk), disable_web_page_preview=True)

# ---------- Pending deposits ----------
@admin_only
async def pending(update, context):
    rows = await DB.fetchall(
        "SELECT id, user_id, method, amount, unique_amount, tx_reference, "
        "       detected_txid, status, created_at, expires_at "
        "FROM deposits "
        "WHERE status IN ('pending','pending_payment','payment_detected','confirming') "
        "ORDER BY id DESC LIMIT 30")
    if not rows:
        await update.message.reply_text("No pending deposits."); return
    lines = ["<b>Pending Deposits</b>", ""]
    for r in rows:
        method = (r["method"] or "").upper()
        tx = r["detected_txid"] or r["tx_reference"] or ""
        ua = r["unique_amount"]
        amt_line = (f"{money(r['amount'])}"
                    + (f"  (exact: <code>{ua}</code>)" if ua else ""))
        lines.append(
            f"#{r['id']} <code>{r['user_id']}</code> [{method}] {amt_line}\n"
            f"  status: <b>{r['status']}</b>"
            + (f" · tx: <code>{tx[:24]}…</code>" if tx else "")
            + f"\n  created: {r['created_at'][:16]}"
            + (f" · expires: {r['expires_at'][:16]}" if r['expires_at'] else "")
        )
    lines.append("\n<i>Crypto deposits (TRC20/BEP20/BTC) are auto-detected. "
                 "Use /approve only for Binance/Stars.</i>")
    await update.message.reply_html("\n".join(lines))

# ---------- Tickets ----------
@admin_only
async def tickets(update, context):
    rows = await DB.fetchall(
        "SELECT id, user_id, message, created_at FROM support_messages "
        "WHERE status='open' ORDER BY id DESC LIMIT 20")
    if not rows:
        await update.message.reply_text("No open tickets."); return
    text = "<b>Open Tickets</b>\n\n" + "\n".join(
        f"#{r['id']} <code>{r['user_id']}</code> {r['created_at'][:16]}\n{(r['message'] or '')[:160]}\n"
        for r in rows)
    await update.message.reply_html(text)

# ---------- Announcements ----------
@admin_only
async def announce(update, context):
    raw = update.message.text.split(" ", 1)
    if len(raw) < 2 or "|" not in raw[1]:
        await update.message.reply_text("Usage: /announce Title | Body text..."); return
    title, body = [s.strip() for s in raw[1].split("|", 1)]
    await DB.execute("INSERT INTO announcements(title, body) VALUES (?, ?)", (title, body))
    await update.message.reply_text("✅ Announcement published.")


# ---------- Category edit/delete ----------
@admin_only
async def rencat(update, context):
    if len(context.args) < 2:
        await update.message.reply_text("Usage: /rencat <id> <new name>"); return
    cid = int(context.args[0])
    name = " ".join(context.args[1:]).strip()
    try:
        await DB.execute("UPDATE categories SET name=? WHERE id=?", (name, cid))
        await update.message.reply_text(f"✅ Category #{cid} renamed to: {name}")
    except Exception:
        await update.message.reply_text("Name already exists.")

@admin_only
async def delcat(update, context):
    if not context.args:
        await update.message.reply_text("Usage: /delcat <id>"); return
    cid = int(context.args[0])
    await DB.execute("DELETE FROM categories WHERE id=?", (cid,))
    await update.message.reply_text(
        f"🗑 Category #{cid} deleted. Products in it are now uncategorized.")

# ---------- Product edit ----------
@admin_only
async def setprice(update, context):
    if len(context.args) != 2:
        await update.message.reply_text("Usage: /setprice <id> <amount>"); return
    pid = int(context.args[0]); price = float(context.args[1])
    await DB.execute("UPDATE products SET price=? WHERE id=?", (price, pid))
    await update.message.reply_text(f"✅ Product #{pid} price = {money(price)}")

@admin_only
async def rename_product(update, context):
    if len(context.args) < 2:
        await update.message.reply_text("Usage: /rename <id> <new name>"); return
    pid = int(context.args[0]); name = " ".join(context.args[1:]).strip()
    await DB.execute("UPDATE products SET name=? WHERE id=?", (name, pid))
    await update.message.reply_text(f"✅ Product #{pid} renamed to: {name}")

@admin_only
async def editdesc(update, context):
    if len(context.args) < 2:
        await update.message.reply_text("Usage: /editdesc <id> <new description>"); return
    pid = int(context.args[0]); desc = " ".join(context.args[1:])
    await DB.execute("UPDATE products SET description=? WHERE id=?", (desc, pid))
    await update.message.reply_text(f"✅ Product #{pid} description updated.")

@admin_only
async def editcontent(update, context):
    """Replace delivery_content (text / link / key) for a product."""
    if len(context.args) < 2:
        await update.message.reply_text("Usage: /editcontent <id> <delivery text>"); return
    pid = int(context.args[0]); content = " ".join(context.args[1:])
    await DB.execute("UPDATE products SET delivery_content=? WHERE id=?", (content, pid))
    await update.message.reply_text(f"✅ Product #{pid} delivery content updated.")
