"""Browse products, pick quantity, confirm, buy from wallet."""
from telegram import Update
from telegram.ext import ContextTypes, CallbackQueryHandler
from database.db import DB
from utils.helpers import money, format_delivery
from utils.keyboards import inline, decorate
from utils.decorators import anti_flood
from services.notifications import notify_staff
from services.rewards import maybe_purchase_reward, mark_active_and_reward

OUT_OF_STOCK_MSG = (
    "❌ <b>Out of Stock</b>\n\n"
    "Sorry, this product is currently <b>out of stock</b>.\n"
    "Please check back soon — we restock regularly! 🚀"
)

def _sort_key(name: str) -> str:
    """Sort A-Z ignoring leading emojis / symbols / spaces."""
    s = name or ""
    i = 0
    while i < len(s) and not s[i].isalnum():
        i += 1
    return s[i:].lower() or s.lower()


@anti_flood
async def categories(update: Update, context: ContextTypes.DEFAULT_TYPE):
    cats = await DB.fetchall("SELECT id, name FROM categories")
    cats = sorted(cats, key=lambda c: _sort_key(c["name"]))
    if not cats:
        text = "🛒 No categories yet. Please check back soon!"
        kb = inline([[("⬅ Back", "menu:home")]])
    else:
        text = "🛒 <b>Choose a category:</b>"
        # 2 categories per row
        rows = []
        row = []
        for i, c in enumerate(cats):
            row.append((decorate(c["name"], i), f"cat:{c['id']}"))
            if len(row) == 2:
                rows.append(row); row = []
        if row:
            rows.append(row)
        rows.append([("⬅ Back", "menu:home")])
        kb = inline(rows)
    if update.callback_query:
        await update.callback_query.answer()
        try:
            await update.callback_query.edit_message_text(text, parse_mode="HTML",
                                                          reply_markup=kb)
        except Exception:
            await update.callback_query.message.reply_html(text, reply_markup=kb)
    else:
        await update.message.reply_html(text, reply_markup=kb)


async def list_products(update: Update, context: ContextTypes.DEFAULT_TYPE):
    q = update.callback_query; await q.answer()
    cat_id = int(q.data.split(":")[1])
    prods = await DB.fetchall(
        "SELECT id, name, price, stock FROM products "
        "WHERE category_id=? AND is_active=1 ORDER BY id DESC", (cat_id,))
    if not prods:
        await q.edit_message_text("No products in this category.",
            reply_markup=inline([[("⬅ Back", "prod:cats")]])); return
    rows = []
    for i, p in enumerate(prods):
        tag = "" if p["stock"] > 0 else " (out of stock)"
        label = decorate(f"{p['name']} — {money(p['price'])}{tag}", i)
        rows.append([(label, f"prod:{p['id']}:1")])
    rows.append([("⬅ Back", "prod:cats")])
    await q.edit_message_text("Select a product:", reply_markup=inline(rows))


def _qty_kb(pid: int, qty: int, price: float, stock: int):
    qty = max(1, min(qty, stock if stock > 0 else 1))
    total = price * qty
    minus = max(1, qty - 1)
    plus = min(stock, qty + 1) if stock > 0 else qty
    return [
        [("➖", f"prod:{pid}:{minus}"),
         (f"Qty: {qty}", "prod:noop"),
         ("➕", f"prod:{pid}:{plus}")],
        [(f"✅ Buy (Total: {money(total)})", f"buyq:{pid}:{qty}")],
        [("⬅ Back", "prod:cats")],
    ]


async def product_detail(update: Update, context: ContextTypes.DEFAULT_TYPE):
    q = update.callback_query; await q.answer()
    parts = q.data.split(":")
    pid = int(parts[1])
    qty = int(parts[2]) if len(parts) > 2 else 1
    p = await DB.fetchone("SELECT * FROM products WHERE id=? AND is_active=1", (pid,))
    if not p:
        await q.edit_message_text("Product not found."); return
    stock = p["stock"]
    text = (
        f"<b>{p['name']}</b>\n\n{p['description'] or ''}\n\n"
        f"💵 Unit Price: <b>{money(p['price'])}</b>\n"
        f"📦 Stock: {stock}\n"
    )
    if stock <= 0:
        text += "\n" + OUT_OF_STOCK_MSG
        kb = inline([[("⬅ Back", "prod:cats")]])
    else:
        kb = inline(_qty_kb(pid, qty, p["price"], stock))
    try:
        await q.edit_message_text(text, parse_mode="HTML", reply_markup=kb)
    except Exception:
        pass


async def noop_cb(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.callback_query.answer()


async def confirm_buy(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Step 1 of buy: show confirmation prompt."""
    q = update.callback_query; await q.answer()
    parts = q.data.split(":")
    pid, qty = int(parts[1]), max(1, int(parts[2]))
    p = await DB.fetchone("SELECT * FROM products WHERE id=? AND is_active=1", (pid,))
    if not p:
        await q.message.reply_text("Product unavailable."); return
    if p["stock"] <= 0:
        await q.edit_message_text(OUT_OF_STOCK_MSG, parse_mode="HTML",
            reply_markup=inline([[("⬅ Back", "prod:cats")]])); return
    if p["stock"] < qty:
        await q.edit_message_text(
            f"⚠ Only <b>{p['stock']}</b> left in stock. Please reduce quantity.",
            parse_mode="HTML",
            reply_markup=inline(_qty_kb(pid, p["stock"], p["price"], p["stock"])))
        return
    total = p["price"] * qty
    text = (
        "🧾 <b>Confirm Your Purchase</b>\n\n"
        f"Product: <b>{p['name']}</b>\n"
        f"Quantity: <b>{qty}</b>\n"
        f"Unit Price: {money(p['price'])}\n"
        f"━━━━━━━━━━━━━━━━━\n"
        f"💳 <b>Total: {money(total)}</b>\n\n"
        "Tap <b>Confirm</b> to deduct from your wallet and receive delivery."
    )
    kb = inline([
        [("✅ Confirm Purchase", f"buy:{pid}:{qty}")],
        [("❌ Cancel", f"prod:{pid}:{qty}")],
    ])
    await q.edit_message_text(text, parse_mode="HTML", reply_markup=kb)


async def buy(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Step 2 of buy: actually deduct & deliver."""
    q = update.callback_query; await q.answer()
    uid = q.from_user.id
    parts = q.data.split(":")
    pid = int(parts[1])
    qty = max(1, int(parts[2]))

    p = await DB.fetchone("SELECT * FROM products WHERE id=? AND is_active=1", (pid,))
    if not p:
        await q.message.reply_text("Product unavailable."); return
    if p["stock"] <= 0:
        await q.message.reply_html(OUT_OF_STOCK_MSG); return
    if p["stock"] < qty:
        await q.message.reply_text(f"❌ Only {p['stock']} in stock."); return

    total = p["price"] * qty
    user = await DB.fetchone("SELECT balance FROM users WHERE user_id=?", (uid,))
    if not user or user["balance"] < total:
        await q.message.reply_html(
            f"❌ <b>Insufficient Balance</b>\n\n"
            f"You need {money(total)}, you have "
            f"{money(user['balance'] if user else 0)}.\nUse 💳 Deposit to add funds.")
        return

    # Atomic decrement — also prevents overselling
    cur = await DB.execute(
        "UPDATE products SET stock = stock - ? WHERE id=? AND stock >= ?",
        (qty, pid, qty))
    if cur.rowcount == 0:
        await q.message.reply_text("❌ Stock just changed. Please try again."); return

    await DB.execute(
        "UPDATE users SET balance = balance - ?, total_purchases = total_purchases + ? "
        "WHERE user_id=?", (total, total, uid))
    await DB.execute(
        "INSERT INTO orders(user_id, product_id, product_name, amount, quantity, "
        "delivery_content) VALUES (?,?,?,?,?,?)",
        (uid, pid, p["name"], total, qty, p["delivery_content"] or ""))

    delivery_html = format_delivery(p["delivery_content"] or "(see file)")
    delivered_text = (
        f"✅ <b>Purchase Successful</b>\n\n"
        f"Product: <b>{p['name']}</b>\n"
        f"Quantity: <b>{qty}</b>\n"
        f"Paid: <b>{money(total)}</b>\n\n"
        f"📦 <b>Your Delivery:</b>\n\n{delivery_html}"
    )
    await q.message.reply_html(delivered_text, disable_web_page_preview=False)
    if p["delivery_file_id"]:
        for _ in range(qty):
            try:
                await q.message.reply_document(p["delivery_file_id"])
            except Exception:
                break

    await notify_staff(context.bot,
        f"🛍 <b>New Purchase</b>\nUser: <code>{uid}</code>\n"
        f"Product: {p['name']} x{qty}\nTotal: {money(total)}")

    await mark_active_and_reward(context.bot, uid)
    await maybe_purchase_reward(context.bot, uid)


def callback_handlers():
    return [
        CallbackQueryHandler(categories,     pattern=r"^prod:cats$"),
        CallbackQueryHandler(noop_cb,        pattern=r"^prod:noop$"),
        CallbackQueryHandler(list_products,  pattern=r"^cat:\d+$"),
        CallbackQueryHandler(product_detail, pattern=r"^prod:\d+(:\d+)?$"),
        CallbackQueryHandler(confirm_buy,    pattern=r"^buyq:\d+:\d+$"),
        CallbackQueryHandler(buy,            pattern=r"^buy:\d+:\d+$"),
    ]
