"use client";

// ─────────────────────────────────────────────────────────────────────────────
// POS v2 — WIRED to live data. Reuses the existing data layer + cart store +
// submit/payment endpoints (shares React Query cache with the classic POS).
// Flow: table board → order screen → pay sheet → back to board.
// Mounted at /[locale]/pos (and /pos2). Design mirrors pos2-prototype.tsx.
// ─────────────────────────────────────────────────────────────────────────────

import { useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  Search, Plus, Minus, Trash2, Send, CreditCard, SplitSquareHorizontal,
  X, WifiOff, ShoppingCart, Users, Coffee, ArrowRight, LayoutGrid, Armchair,
  Banknote, Check, Delete, ReceiptText, ShoppingBag, Loader2,
  BadgePercent, Sparkles, Home, StickyNote,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { notify } from "@/lib/notify";
import { useRouter } from "@/i18n/routing";
import { useAuthStore } from "@/lib/stores/auth.store";
import { useBranchStore } from "@/lib/stores/branch.store";
import { useCartStore } from "@/lib/stores/cart.store";
import { apiGet, apiPost, ApiClientError } from "@/lib/api/client";
import { submitOrderToApi, orderAmountDue, isLocalOrder } from "@/lib/pos2/submit-order";
import { requestPosPayment, posDeviceErrorMessage } from "@/lib/api/pos-device";
import { printReceipt } from "@/lib/api/print";
import { PosCustomerPicker } from "@/components/pos2/pos-customer-picker";
import { Can } from "@/components/auth/can";
import type { Customer, MenuItem, Order, TableBoardItem } from "@/lib/api/types";
import { usePos2Categories, usePos2Menu, usePos2Tables, useMenuById } from "@/lib/pos2/use-pos2";

const fmt = (n: number) => Math.round(n).toLocaleString("fa-IR");
const TAX = 0.09;
const POINT_VALUE = 100; // 1 loyalty point = 100 toman (matches classic POS)
type Method = "Cash" | "Card" | "Credit";
type Payment = { method: Method; amount: number };

const errMsg = (e: unknown, fb: string) => (e instanceof ApiClientError ? e.message || fb : fb);

const COUPON_FA: Record<string, string> = {
  COUPON_NOT_FOUND: "کد تخفیف نامعتبر است",
  COUPON_INACTIVE: "کد تخفیف غیرفعال است",
  COUPON_EXPIRED: "کد تخفیف منقضی شده است",
  COUPON_NOT_STARTED: "کد تخفیف هنوز فعال نشده است",
  COUPON_LIMIT_REACHED: "سقف استفاده از این کد پر شده است",
  COUPON_MIN_ORDER: "حداقل مبلغ سفارش رعایت نشده است",
  CART_EMPTY: "سبد خالی است",
  COUPON_REQUIRED: "کد تخفیف را وارد کنید",
  COUPON_NO_DISCOUNT: "کد تخفیف نامعتبر است",
};
const couponErr = (e: unknown) =>
  e instanceof ApiClientError ? COUPON_FA[e.code] ?? "کد تخفیف نامعتبر است" : "کد تخفیف نامعتبر است";

const POS_DEVICE_FA: Record<string, string> = {
  posDeviceNotConfigured: "دستگاه کارت‌خوان تنظیم نشده است",
  posDeviceConnectionFailed: "اتصال به کارت‌خوان ناموفق بود",
  posDeviceTimeout: "زمان پاسخ کارت‌خوان به پایان رسید",
  posDeviceRejected: "تراکنش توسط کارت‌خوان رد شد",
  posDeviceError: "خطای کارت‌خوان",
};
const posDeviceMsg = (e: unknown) => posDeviceErrorMessage(e, (k) => POS_DEVICE_FA[k] ?? "خطای کارت‌خوان");

export function Pos2Screen() {
  const router = useRouter();
  const queryClient = useQueryClient();
  const cafeId = useAuthStore((s) => s.user?.cafeId);
  const branchId = useBranchStore((s) => s.branchId);
  const setBranchId = useBranchStore((s) => s.setBranchId);

  // Resolve a VALID branch (auto-pick the first) exactly like the classic POS —
  // the menu/tables are branch-scoped, so a null or stale stored branchId would
  // otherwise load an empty menu. v2 has no branch picker, so it must self-heal.
  //
  // IMPORTANT: orderBranchId returns `undefined` while branches are still loading.
  // usePos2Menu treats `undefined` as "not yet determined" and pauses the query so
  // we never fire getBranchMenu(cafeId, null) which returns an empty array.
  const { data: branches = [], isFetched: branchesFetched } = useQuery({
    queryKey: ["branches", cafeId],
    queryFn: () => apiGet<{ id: string; name: string }[]>(`/api/cafes/${cafeId}/branches`),
    enabled: !!cafeId,
  });
  useEffect(() => {
    if (!branchesFetched || branches.length === 0) return;
    const valid = branchId && branches.some((b) => b.id === branchId);
    if (!valid) setBranchId(branches[0]!.id);
  }, [branchesFetched, branches, branchId, setBranchId]);
  const orderBranchId = useMemo<string | null | undefined>(() => {
    if (!branchesFetched) return undefined; // still loading → pause the menu query
    if (branchId && branches.some((b) => b.id === branchId)) return branchId;
    return branches[0]?.id ?? null; // null = no branches → café-wide fallback
  }, [branchesFetched, branchId, branches]);

  const { data: categories } = usePos2Categories(cafeId);
  const { data: menu, isLoading: menuLoading, isError: menuError, refetch: refetchMenu } = usePos2Menu(cafeId, orderBranchId);
  const { data: tables, isLoading: tablesLoading, refetch: refetchTables } = usePos2Tables(cafeId, orderBranchId);
  const menuById = useMenuById(menu);

  // cart store slices
  const items = useCartStore((s) => s.items);
  const syncedQty = useCartStore((s) => s.syncedQtyByMenuId);
  const addItem = useCartStore((s) => s.addItem);
  const updateQty = useCartStore((s) => s.updateQty);
  const removeItem = useCartStore((s) => s.removeItem);
  const setNotes = useCartStore((s) => s.setNotes);
  const setTableId = useCartStore((s) => s.setTableId);
  const setOrderType = useCartStore((s) => s.setOrderType);
  const hydrateFromOrder = useCartStore((s) => s.hydrateFromOrder);
  const clearSession = useCartStore((s) => s.clearSession);
  const activeOrderNo = useCartStore((s) => s.activeOrderDisplayNumber);
  const activeOrderId = useCartStore((s) => s.activeOrderId);
  const appliedCoupon = useCartStore((s) => s.appliedCoupon);

  // local view state
  const [view, setView] = useState<"board" | "order">("board");
  const [activeTable, setActiveTable] = useState<TableBoardItem | null>(null);
  const [takeaway, setTakeaway] = useState(false);
  const [cat, setCat] = useState("all");
  const [q, setQ] = useState("");
  const [cartOpen, setCartOpen] = useState(false);
  const [busy, setBusy] = useState(false);
  const [payTarget, setPayTarget] = useState<Order | null>(null);
  const [payLoyalty, setPayLoyalty] = useState(0);
  // Order just paid — kept after the cart is cleared so the receipt stays printable.
  const [paidOrderId, setPaidOrderId] = useState<string | null>(null);
  const payingRef = useRef(false); // re-entry guard for the payment confirm

  const [online, setOnline] = useState(true);
  useEffect(() => {
    const u = () => setOnline(typeof navigator === "undefined" ? true : navigator.onLine);
    u();
    window.addEventListener("online", u);
    window.addEventListener("offline", u);
    return () => { window.removeEventListener("online", u); window.removeEventListener("offline", u); };
  }, []);

  const live = items.filter((l) => !l.isVoided);
  const subtotal = live.reduce((s, l) => s + l.menuItem.price * l.quantity, 0);
  const discount = appliedCoupon?.discountAmount ?? 0;
  const taxable = Math.max(0, subtotal - discount);
  const tax = Math.round(taxable * TAX);
  const total = taxable + tax;
  const count = live.reduce((s, l) => s + l.quantity, 0);
  const pendingCount = items.reduce(
    (n, l) => n + Math.max(0, l.quantity - (syncedQty[l.menuItem.id] ?? 0)),
    0,
  );

  const visibleItems = useMemo(() => {
    const list = (menu ?? []).filter((i) => i.isAvailable !== false);
    return list.filter(
      (i) =>
        (cat === "all" || i.categoryId === cat) &&
        (q === "" || i.name.includes(q) || (i.nameEn ?? "").toLowerCase().includes(q.toLowerCase())),
    );
  }, [menu, cat, q]);

  const catChips = useMemo(
    () => [{ id: "all", name: "همه" }, ...(categories ?? []).map((c) => ({ id: c.id, name: c.name }))],
    [categories],
  );

  // ── navigation ───────────────────────────────────────────────────────────
  const openFreeTable = (t: TableBoardItem) => {
    clearSession();
    setTableId(t.id);
    setOrderType("table");
    setActiveTable(t); setTakeaway(false); setCat("all"); setQ(""); setCartOpen(false);
    setView("order");
  };

  const openBusyTable = async (t: TableBoardItem) => {
    const oid = t.currentOrder?.orderId;
    if (!oid) return openFreeTable(t);
    setBusy(true);
    try {
      const order = await apiGet<Order>(`/api/cafes/${cafeId}/orders/${oid}`);
      hydrateFromOrder(order, menuById);
      setOrderType("table");
      setActiveTable(t); setTakeaway(false); setCat("all"); setQ(""); setCartOpen(false);
      setView("order");
    } catch (e) {
      notify.error(errMsg(e, "بارگذاری سفارش میز ناموفق بود"));
    } finally {
      setBusy(false);
    }
  };

  const openTakeaway = () => {
    clearSession();
    setOrderType("takeaway");
    setActiveTable(null); setTakeaway(true); setCat("all"); setQ(""); setCartOpen(false);
    setView("order");
  };

  const backToBoard = () => {
    clearSession();
    setActiveTable(null); setTakeaway(false); setPayTarget(null); setCartOpen(false);
    setView("board");
    refetchTables();
  };

  // ── actions ──────────────────────────────────────────────────────────────
  const submitPending = async (): Promise<Order | null> => {
    const cart = useCartStore.getState();
    if (cart.getPendingLines().length === 0) return null;
    const order = await submitOrderToApi({
      cafeId: cafeId as string,
      orderBranchId: orderBranchId ?? undefined,
      cart,
      reservationId: null,
      cartItems: cart.items,
    });
    hydrateFromOrder(order, menuById);
    queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
    return order;
  };

  const send = async () => {
    if (pendingCount === 0) { notify.error("آیتمی برای ارسال نیست"); return; }
    setBusy(true);
    try {
      const order = await submitPending();
      if (order) {
        notify.success(
          isLocalOrder(order.id)
            ? "سفارش آفلاین ذخیره شد و هنگام اتصال ارسال می‌شود"
            : "سفارش به آشپزخانه ارسال شد",
        );
      }
    } catch (e) {
      notify.error(errMsg(e, "ارسال سفارش ناموفق بود"));
    } finally {
      setBusy(false);
    }
  };

  const openPay = async () => {
    if (count === 0) { notify.error("سبد خالی است"); return; }
    setBusy(true);
    try {
      let order = await submitPending();
      if (!order) {
        const cart = useCartStore.getState();
        if (cart.activeOrderId && !isLocalOrder(cart.activeOrderId)) {
          order = await apiGet<Order>(`/api/cafes/${cafeId}/orders/${cart.activeOrderId}`);
        }
      }
      if (!order) {
        const cart = useCartStore.getState();
        order = {
          id: cart.activeOrderId ?? "local_pending",
          cafeId: cafeId as string,
          orderType: "DineIn", status: "Open",
          subtotal, taxTotal: tax, discountAmount: discount, total,
          paidAmount: 0, createdAt: new Date().toISOString(), displayNumber: 0,
          items: [], payments: [],
        };
      }
      // loyalty points available on the attached customer (1 pt = 100 toman)
      const customerId = useCartStore.getState().customerId;
      let pts = 0;
      if (customerId && !isLocalOrder(order.id)) {
        try {
          const c = await apiGet<Customer>(`/api/cafes/${cafeId}/customers/${customerId}`);
          pts = c.loyaltyPoints ?? 0;
        } catch { pts = 0; }
      }
      setPayLoyalty(pts);
      setPayTarget(order);
    } catch (e) {
      notify.error(errMsg(e, "آماده‌سازی پرداخت ناموفق بود"));
    } finally {
      setBusy(false);
    }
  };

  const confirmPay = async (payments: Payment[], loyaltyRedeem: number) => {
    if (!payTarget || payingRef.current) return; // guard against a double-tap
    payingRef.current = true;
    setBusy(true);
    try {
      const cardTotal = payments.filter((p) => p.method === "Card").reduce((s, p) => s + p.amount, 0);
      const payBranchId = payTarget.branchId ?? orderBranchId ?? undefined;
      if (cardTotal > 0 && payBranchId) {
        // push the card amount to the configured terminal (no-op/skip if none)
        await requestPosPayment(cafeId as string, payBranchId, payTarget.id, cardTotal);
      }
      await apiPost(`/api/cafes/${cafeId}/orders/${payTarget.id}/payments`, {
        payments,
        loyaltyPointsToRedeem: loyaltyRedeem > 0 ? loyaltyRedeem : undefined,
      });
      const paid = payments.reduce((s, p) => s + p.amount, 0);
      const justPaidOrderId = payTarget.id;
      notify.success(`پرداخت ${fmt(paid)} تومان ثبت شد`);
      queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
      // Clear the cart + close the pay sheet, but STAY on the order view so the
      // payment-success sheet (which lives in this view) renders and the cashier
      // can print the receipt. Going back to the board happens on dismiss.
      clearSession();
      setPayTarget(null);
      setCartOpen(false);
      if (!isLocalOrder(justPaidOrderId)) setPaidOrderId(justPaidOrderId);
      else backToBoard();
    } catch (e) {
      if (e instanceof ApiClientError && e.code.startsWith("POS_DEVICE")) {
        notify.error(posDeviceMsg(e));
      } else if (e instanceof ApiClientError && e.code === "NO_OPEN_SHIFT") {
        notify.error("برای پرداخت باید شیفت باز باشد");
      } else if (e instanceof ApiClientError && e.code === "ORDER_ALREADY_CLOSED") {
        notify.error("این سفارش قبلاً تسویه شده است");
      } else {
        notify.error(errMsg(e, "ثبت پرداخت ناموفق بود"));
      }
    } finally {
      payingRef.current = false;
      setBusy(false);
    }
  };

  // Print (or reprint) the customer receipt for a saved server order.
  const printReceiptById = async (orderId: string) => {
    try {
      await printReceipt(cafeId as string, orderId);
      notify.success("فاکتور برای چاپ ارسال شد");
    } catch (e) {
      const code = e instanceof ApiClientError ? e.code : "";
      notify.error(
        code === "PRINTER_NOT_CONFIGURED" || code === "KITCHEN_PRINTER_NOT_CONFIGURED"
          ? "پرینتر فاکتور تنظیم نشده است"
          : code === "PRINTER_CONNECTION_FAILED"
            ? "اتصال به پرینتر برقرار نشد"
            : "چاپ فاکتور ناموفق بود",
      );
    }
  };

  const printActiveReceipt = async () => {
    if (!activeOrderId || isLocalOrder(activeOrderId)) {
      notify.error("ابتدا سفارش را ثبت کنید");
      return;
    }
    await printReceiptById(activeOrderId);
  };

  // ── guards ───────────────────────────────────────────────────────────────
  if (!cafeId) {
    return (
      <div className="flex h-svh items-center justify-center text-muted-foreground">
        <Loader2 className="size-6 animate-spin" />
      </div>
    );
  }

  const offlineBadge = !online && (
    <span className="flex items-center gap-1.5 rounded-lg bg-amber-100 px-2.5 py-1.5 text-xs font-medium text-amber-800">
      <WifiOff className="size-4" /> آفلاین
    </span>
  );

  const ticketProps = {
    cafeId,
    // mark fully-sent lines so their note becomes read-only (a note-only change on
    // an already-sent line would otherwise be silently dropped on the next send).
    lines: live.map((l) => ({ ...l, synced: (syncedQty[l.menuItem.id] ?? 0) >= l.quantity })),
    subtotal, discount, tax, total, count, pendingCount,
    onBump: (id: string, d: number) => { const l = items.find((x) => x.menuItem.id === id); if (l) updateQty(id, l.quantity + d); },
    onRemove: removeItem, onSend: send, onPay: openPay, onSplit: openPay,
    onNote: (id: string, notes: string) => setNotes(id, notes),
    canPrint: !!activeOrderId && !isLocalOrder(activeOrderId),
    onPrintReceipt: printActiveReceipt,
  };

  // ── TABLE BOARD ────────────────────────────────────────────────────────────
  if (view === "board") {
    const list = tables ?? [];
    const occupied = list.filter((t) => t.status === "Busy").length;
    return (
      <div dir="rtl" className="flex h-svh min-h-0 flex-col bg-background text-foreground">
        {busy && <BusyOverlay />}
        <header className="flex shrink-0 items-center gap-3 border-b border-border bg-card px-4 py-3">
          {/* Dashboard exit — lets user navigate back without the sidebar */}
          <button
            type="button"
            onClick={() => router.push("/")}
            className="flex min-h-[40px] cursor-pointer items-center gap-1.5 rounded-xl px-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent"
            title="بازگشت به داشبورد"
          >
            <Home className="size-4" />
            <span className="hidden sm:inline">داشبورد</span>
          </button>
          <div className="h-5 w-px bg-border" />
          <div className="flex items-center gap-2 text-primary">
            <LayoutGrid className="size-6" />
            <span className="text-lg font-bold">میزها</span>
          </div>
          <span className="hidden rounded-lg bg-muted px-2.5 py-1 text-sm text-muted-foreground sm:inline">
            {fmt(occupied)} فعال · {fmt(Math.max(0, list.length - occupied))} خالی
          </span>
          <div className="flex-1" />
          {offlineBadge}
          <button
            type="button"
            onClick={openTakeaway}
            className="flex min-h-[44px] cursor-pointer items-center gap-2 rounded-xl bg-primary px-4 font-bold text-primary-foreground transition-colors hover:bg-primary/90 active:scale-[0.98]"
          >
            <ShoppingBag className="size-5" /> بیرون‌بر
          </button>
        </header>

        {tablesLoading ? (
          <div className="flex flex-1 items-center justify-center text-muted-foreground">
            <Loader2 className="size-6 animate-spin" />
          </div>
        ) : list.length === 0 ? (
          <div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center text-muted-foreground">
            <Armchair className="size-12 opacity-40" />
            <p>هنوز میزی تعریف نشده است.</p>
            <button type="button" onClick={openTakeaway} className="rounded-xl bg-primary px-5 py-2.5 font-bold text-primary-foreground">
              شروع سفارش بیرون‌بر
            </button>
          </div>
        ) : (
          <div className="grid min-h-0 flex-1 auto-rows-min grid-cols-2 gap-3 overflow-y-auto p-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
            {list.map((t) => {
              const busyT = t.status === "Busy";
              const reserved = t.status === "Reserved";
              const cleaning = t.status === "Cleaning" || t.isCleaning;
              const open = () => (busyT ? openBusyTable(t) : openFreeTable(t));
              return (
                <button
                  key={t.id}
                  type="button"
                  onClick={open}
                  className={cn(
                    "flex min-h-[124px] cursor-pointer flex-col justify-between rounded-2xl border-2 p-3 text-start transition-all hover:shadow-md active:scale-[0.97]",
                    busyT ? "border-primary/40 bg-primary/5"
                      : reserved ? "border-amber-300 bg-amber-50"
                      : cleaning ? "border-border bg-muted/50"
                      : "border-border bg-card hover:border-primary/40",
                  )}
                >
                  <div className="flex items-center justify-between">
                    <span className="text-lg font-extrabold">میز {t.number}</span>
                    <Armchair className={cn("size-5", busyT ? "text-primary" : "text-muted-foreground/50")} />
                  </div>
                  <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
                    <Users className="size-3.5" /> ظرفیت {fmt(t.capacity)}
                    {t.sectionName ? <span className="truncate">· {t.sectionName}</span> : null}
                  </div>
                  {busyT ? (
                    <div className="flex items-center justify-between">
                      {t.currentOrder?.source === "GuestQr" ? (
                        <span className="inline-flex items-center gap-1 rounded-md bg-primary/15 px-1.5 py-0.5 text-[11px] font-bold text-primary">
                          <ReceiptText className="size-3" /> مهمان
                        </span>
                      ) : <span className="text-xs text-muted-foreground">باز</span>}
                      <span className="text-sm font-extrabold text-primary">{fmt(t.currentOrder?.total ?? 0)}</span>
                    </div>
                  ) : (
                    <span className={cn("text-sm font-medium", reserved ? "text-amber-700" : "text-muted-foreground")}>
                      {reserved ? "رزرو" : cleaning ? "در حال تمیزکاری" : "خالی"}
                    </span>
                  )}
                </button>
              );
            })}
          </div>
        )}
      </div>
    );
  }

  // ── ORDER SCREEN ───────────────────────────────────────────────────────────
  const title = takeaway ? "بیرون‌بر" : `میز ${activeTable?.number ?? ""}`;
  return (
    <div dir="rtl" className="flex h-svh min-h-0 flex-col bg-background text-foreground">
      {busy && <BusyOverlay />}
      <header className="flex shrink-0 items-center gap-3 border-b border-border bg-card px-3 py-2.5">
        <button
          type="button"
          onClick={backToBoard}
          className="flex min-h-[44px] cursor-pointer items-center gap-1.5 rounded-xl bg-muted px-3 font-medium text-muted-foreground transition-colors hover:bg-accent active:scale-95"
        >
          <ArrowRight className="size-5" /> <span className="hidden sm:inline">میزها</span>
        </button>
        <div className="flex items-center gap-2 rounded-xl bg-primary/10 px-3 py-1.5 text-primary">
          {takeaway ? <ShoppingBag className="size-5" /> : <Coffee className="size-5" />}
          <span className="font-bold">{title}</span>
        </div>
        {activeOrderNo ? (
          <span className="hidden rounded-lg bg-muted px-2 py-1 text-xs font-bold text-muted-foreground sm:inline">
            سفارش #{fmt(activeOrderNo)}
          </span>
        ) : null}
        <div className="relative mx-1 flex-1">
          <Search className="pointer-events-none absolute end-3 top-1/2 size-5 -translate-y-1/2 text-muted-foreground" />
          <input
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="جستجوی آیتم…"
            className="h-11 w-full rounded-xl border border-border bg-background pe-10 ps-4 text-base outline-none focus:ring-2 focus:ring-primary/40"
          />
        </div>
        {offlineBadge}
      </header>

      <div className="flex min-h-0 flex-1">
        {/* ── Left: vertical category sidebar (desktop) ── */}
        <nav className="hidden w-[116px] shrink-0 flex-col gap-0.5 overflow-y-auto border-e border-border bg-card p-2 md:flex">
          {catChips.map((c) => (
            <button
              key={c.id}
              type="button"
              onClick={() => setCat(c.id)}
              className={cn(
                "w-full cursor-pointer rounded-xl px-2 py-3 text-center text-xs font-semibold leading-tight transition-colors",
                cat === c.id
                  ? "bg-primary text-primary-foreground shadow-sm"
                  : "text-muted-foreground hover:bg-accent hover:text-foreground",
              )}
            >
              {c.name}
            </button>
          ))}
        </nav>

        {/* ── Center: menu items ── */}
        <main className="flex min-h-0 min-w-0 flex-1 flex-col">
          {/* Horizontal chips — mobile only */}
          <div className="flex shrink-0 gap-2 overflow-x-auto border-b border-border px-4 py-2.5 md:hidden">
            {catChips.map((c) => (
              <button
                key={c.id}
                type="button"
                onClick={() => setCat(c.id)}
                className={cn(
                  "min-h-[44px] shrink-0 cursor-pointer rounded-full px-4 py-2.5 text-sm font-medium transition-colors",
                  cat === c.id ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-accent",
                )}
              >
                {c.name}
              </button>
            ))}
          </div>

          {menuLoading ? (
            <div className="flex flex-1 items-center justify-center text-muted-foreground">
              <Loader2 className="size-6 animate-spin" />
            </div>
          ) : menuError ? (
            <div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center text-muted-foreground">
              <p>بارگذاری منو ناموفق بود.</p>
              <button type="button" onClick={() => refetchMenu()} className="rounded-xl bg-primary px-5 py-2.5 font-bold text-primary-foreground">
                تلاش دوباره
              </button>
            </div>
          ) : (
            <div className="grid min-h-0 flex-1 auto-rows-min grid-cols-2 gap-3 overflow-y-auto p-3 sm:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4">
              {visibleItems.map((it) => (
                <button
                  key={it.id}
                  type="button"
                  onClick={() => addItem(it)}
                  className="flex min-h-[104px] cursor-pointer flex-col justify-between rounded-2xl border border-border bg-card p-3 text-start shadow-sm transition-all hover:border-primary hover:shadow-md active:scale-[0.97]"
                >
                  <div className="flex size-10 items-center justify-center rounded-xl bg-primary/10 text-primary">
                    <Coffee className="size-5" />
                  </div>
                  <div>
                    <p className="line-clamp-1 font-semibold">{it.name}</p>
                    <p className="mt-0.5 text-sm font-bold text-primary">{fmt(it.price)}</p>
                  </div>
                </button>
              ))}
              {visibleItems.length === 0 && (
                <p className="col-span-full py-10 text-center text-muted-foreground">آیتمی یافت نشد</p>
              )}
            </div>
          )}
        </main>

        {/* ── Right: order ticket (desktop) ── */}
        <aside className="hidden w-[360px] shrink-0 border-s border-border bg-card lg:flex lg:flex-col">
          <Ticket {...ticketProps} />
        </aside>
      </div>

      {count > 0 && (
        <button
          type="button"
          onClick={() => setCartOpen(true)}
          className="flex shrink-0 cursor-pointer items-center justify-between gap-3 bg-primary px-4 py-3 text-primary-foreground lg:hidden"
        >
          <span className="flex items-center gap-2 font-bold">
            <ShoppingCart className="size-5" />
            <span className="flex size-6 items-center justify-center rounded-full bg-white/25 text-sm">{fmt(count)}</span>
            مشاهده سفارش
          </span>
          <span className="text-lg font-extrabold">{fmt(total)} تومان</span>
        </button>
      )}

      {cartOpen && (
        <div className="fixed inset-0 z-50 lg:hidden">
          <div className="absolute inset-0 bg-black/40" onClick={() => setCartOpen(false)} />
          <div className="absolute inset-y-0 end-0 flex w-full max-w-sm flex-col bg-card shadow-xl" dir="rtl">
            <div className="flex items-center justify-between border-b border-border px-4 py-3">
              <span className="font-bold">سفارش {title}</span>
              <button type="button" onClick={() => setCartOpen(false)} className="rounded-lg p-2 hover:bg-accent">
                <X className="size-5" />
              </button>
            </div>
            <Ticket {...ticketProps} />
          </div>
        </div>
      )}

      {payTarget && (
        <Pos2PaySheet
          tableName={title}
          amountDue={orderAmountDue(payTarget) || total}
          loyaltyPoints={payLoyalty}
          onClose={() => setPayTarget(null)}
          onConfirm={confirmPay}
        />
      )}

      {paidOrderId && (
        <div dir="rtl" className="fixed inset-0 z-[65] flex items-center justify-center p-4">
          <div className="absolute inset-0 bg-black/50" onClick={() => { setPaidOrderId(null); backToBoard(); }} />
          <div className="relative w-full max-w-sm rounded-2xl bg-card p-6 text-center shadow-2xl">
            <div className="mx-auto mb-3 flex size-14 items-center justify-center rounded-full bg-emerald-100 text-emerald-600">
              <Check className="size-7" />
            </div>
            <p className="text-lg font-bold">پرداخت با موفقیت ثبت شد</p>
            <p className="mt-1 text-sm text-muted-foreground">فاکتور مشتری را می‌توانید چاپ کنید</p>
            <div className="mt-5 space-y-2">
              <button
                type="button"
                onClick={() => void printReceiptById(paidOrderId)}
                className="flex min-h-[52px] w-full items-center justify-center gap-2 rounded-xl bg-primary text-base font-bold text-primary-foreground transition-colors hover:bg-primary/90 active:scale-[0.99]"
              >
                <ReceiptText className="size-5" /> چاپ فاکتور
              </button>
              <button
                type="button"
                onClick={() => { setPaidOrderId(null); backToBoard(); }}
                className="flex min-h-[48px] w-full items-center justify-center rounded-xl bg-muted text-sm font-medium text-foreground hover:bg-accent"
              >
                سفارش جدید
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Customer + coupon controls (self-contained: reads/writes the cart store) ──
function Pos2Extras({ cafeId }: { cafeId: string }) {
  const guestName = useCartStore((s) => s.guestName);
  const guestPhone = useCartStore((s) => s.guestPhone);
  const customerId = useCartStore((s) => s.customerId);
  const setGuestName = useCartStore((s) => s.setGuestName);
  const setGuestPhone = useCartStore((s) => s.setGuestPhone);
  const setCustomer = useCartStore((s) => s.setCustomer);
  const clearCustomer = useCartStore((s) => s.clearCustomer);
  const couponCode = useCartStore((s) => s.couponCode);
  const appliedCoupon = useCartStore((s) => s.appliedCoupon);
  const setCouponCode = useCartStore((s) => s.setCouponCode);
  const setAppliedCoupon = useCartStore((s) => s.setAppliedCoupon);
  const clearCoupon = useCartStore((s) => s.clearCoupon);
  const subtotalFn = useCartStore((s) => s.subtotal);
  const [msg, setMsg] = useState<string | null>(null);

  const apply = useMutation({
    mutationFn: () =>
      apiPost<{ couponId: string; code: string; discountAmount: number }>(
        `/api/cafes/${cafeId}/coupons/validate`,
        { code: couponCode.trim(), subtotal: subtotalFn() },
      ),
    onSuccess: (d) => {
      setAppliedCoupon({ id: d.couponId, code: d.code, discountAmount: d.discountAmount });
      setMsg(null);
    },
    onError: (e) => { setAppliedCoupon(null); setMsg(couponErr(e)); },
  });

  return (
    <div className="shrink-0 space-y-2 border-t border-border px-3 py-2">
      <PosCustomerPicker
        cafeId={cafeId}
        guestName={guestName}
        guestPhone={guestPhone}
        customerId={customerId}
        onGuestNameChange={setGuestName}
        onGuestPhoneChange={setGuestPhone}
        onCustomerChange={setCustomer}
        onClearCustomer={clearCustomer}
        compact
      />

      {appliedCoupon ? (
        <div className="flex items-center justify-between gap-2 rounded-lg border border-emerald-300 bg-emerald-50 px-2.5 py-2 text-sm">
          <span className="flex items-center gap-1.5 font-medium text-emerald-700">
            <BadgePercent className="size-4" /> {appliedCoupon.code}
            <span className="text-xs text-emerald-600">(−{fmt(appliedCoupon.discountAmount)})</span>
          </span>
          <button type="button" onClick={() => { clearCoupon(); setMsg(null); }} className="rounded-md p-1 text-emerald-700 hover:bg-emerald-100" aria-label="حذف کوپن">
            <X className="size-4" />
          </button>
        </div>
      ) : (
        <div className="flex items-center gap-2">
          <div className="relative flex-1">
            <BadgePercent className="pointer-events-none absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
            <input
              value={couponCode}
              onChange={(e) => { setCouponCode(e.target.value); setMsg(null); }}
              onKeyDown={(e) => { if (e.key === "Enter" && couponCode.trim() && !apply.isPending) apply.mutate(); }}
              placeholder="کد تخفیف"
              className="h-10 w-full rounded-lg border border-border bg-background pe-8 ps-3 text-sm outline-none focus:ring-2 focus:ring-primary/40"
            />
          </div>
          <button
            type="button"
            disabled={!couponCode.trim() || apply.isPending}
            onClick={() => apply.mutate()}
            className="flex h-10 min-w-[64px] items-center justify-center rounded-lg bg-muted px-3 text-sm font-medium hover:bg-accent disabled:opacity-40"
          >
            {apply.isPending ? <Loader2 className="size-4 animate-spin" /> : "اعمال"}
          </button>
        </div>
      )}
      {msg ? <p className="text-xs text-red-500">{msg}</p> : null}
    </div>
  );
}

// ── Order ticket ─────────────────────────────────────────────────────────────
type TicketLine = { menuItem: MenuItem; quantity: number; notes?: string; synced?: boolean };
function Ticket({
  cafeId, lines, subtotal, discount, tax, total, count, pendingCount, onBump, onRemove, onNote, onSend, onPay, onSplit, canPrint, onPrintReceipt,
}: {
  cafeId: string; lines: TicketLine[]; subtotal: number; discount: number; tax: number; total: number;
  count: number; pendingCount: number;
  onBump: (id: string, d: number) => void; onRemove: (id: string) => void;
  onNote: (id: string, notes: string) => void;
  onSend: () => void; onPay: () => void; onSplit: () => void;
  canPrint: boolean; onPrintReceipt: () => void;
}) {
  const [noteFor, setNoteFor] = useState<string | null>(null);
  return (
    <div className="flex min-h-0 flex-1 flex-col">
      <div className="min-h-0 flex-1 overflow-y-auto p-3">
        {lines.length === 0 ? (
          <div className="flex h-full flex-col items-center justify-center gap-2 text-muted-foreground">
            <ShoppingCart className="size-10 opacity-40" />
            <p>سبد خالی است</p>
            <p className="text-xs">برای افزودن، روی آیتم‌ها بزنید</p>
          </div>
        ) : (
          <ul className="space-y-2">
            {lines.map((l) => (
              <li key={l.menuItem.id} className="rounded-xl border border-border/70 p-2">
                <div className="flex items-center gap-2">
                  <div className="min-w-0 flex-1">
                    <p className="line-clamp-1 font-medium">{l.menuItem.name}</p>
                    <p className="text-xs text-muted-foreground">{fmt(l.menuItem.price)} تومان</p>
                  </div>
                  <div className="flex items-center gap-1">
                    <button type="button" onClick={() => onBump(l.menuItem.id, -1)} className="flex size-11 items-center justify-center rounded-lg bg-muted hover:bg-accent active:scale-95" aria-label="کم">
                      <Minus className="size-4" />
                    </button>
                    <span className="w-7 text-center font-bold">{fmt(l.quantity)}</span>
                    <button type="button" onClick={() => onBump(l.menuItem.id, 1)} className="flex size-11 items-center justify-center rounded-lg bg-primary/10 text-primary hover:bg-primary/20 active:scale-95" aria-label="زیاد">
                      <Plus className="size-4" />
                    </button>
                  </div>
                  <button type="button" onClick={() => onRemove(l.menuItem.id)} className="flex size-9 items-center justify-center rounded-lg text-red-500 hover:bg-red-50" aria-label="حذف">
                    <Trash2 className="size-4" />
                  </button>
                </div>
                {l.synced ? (
                  // Already sent to the kitchen — note is read-only (can't be changed now).
                  l.notes ? (
                    <p className="mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground">
                      <StickyNote className="size-3.5 shrink-0" /> {l.notes}
                    </p>
                  ) : null
                ) : noteFor === l.menuItem.id || l.notes ? (
                  <input
                    value={l.notes ?? ""}
                    onChange={(e) => onNote(l.menuItem.id, e.target.value)}
                    onBlur={() => { if (!l.notes) setNoteFor((cur) => (cur === l.menuItem.id ? null : cur)); }}
                    placeholder="یادداشت برای آشپزخانه (مثلاً بدون شکر)"
                    autoFocus={noteFor === l.menuItem.id}
                    maxLength={200}
                    dir="rtl"
                    className="mt-2 w-full rounded-lg border border-border/70 bg-background px-2.5 py-1.5 text-sm outline-none focus:border-primary"
                  />
                ) : (
                  <button
                    type="button"
                    onClick={() => setNoteFor(l.menuItem.id)}
                    className="mt-1.5 flex items-center gap-1.5 rounded-lg px-2 py-1 text-xs font-medium text-primary hover:bg-primary/10"
                  >
                    <StickyNote className="size-3.5" /> افزودن یادداشت
                  </button>
                )}
              </li>
            ))}
          </ul>
        )}
      </div>

      <Pos2Extras cafeId={cafeId} />

      <div className="space-y-1 border-t border-border px-4 py-3 text-sm">
        <Row label="جمع" value={`${fmt(subtotal)} تومان`} />
        {discount > 0 && <Row label="تخفیف" value={`−${fmt(discount)} تومان`} accent />}
        <Row label="مالیات ۹٪" value={`${fmt(tax)} تومان`} />
        <div className="flex items-center justify-between pt-1 text-lg font-extrabold">
          <span>مبلغ کل</span>
          <span className="text-primary">{fmt(total)} تومان</span>
        </div>
      </div>

      <div className="grid grid-cols-2 gap-2 border-t border-border p-3">
        <button type="button" disabled={pendingCount === 0} onClick={onSend}
          className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl bg-primary text-base font-bold text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-40 active:scale-[0.98]">
          <Send className="size-5" /> ارسال{pendingCount > 0 ? ` (${fmt(pendingCount)})` : ""}
        </button>
        <Can permission="HandlePayments">
          <button type="button" disabled={count === 0} onClick={onPay}
            className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl border-2 border-primary text-base font-bold text-primary transition-colors hover:bg-primary/10 disabled:opacity-40 active:scale-[0.98]">
            <CreditCard className="size-5" /> پرداخت
          </button>
        </Can>
        <button type="button" disabled={!canPrint} onClick={onPrintReceipt}
          className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-foreground hover:bg-accent disabled:opacity-40"
          title="چاپ فاکتور مشتری">
          <ReceiptText className="size-4" /> چاپ فاکتور
        </button>
        <Can permission="HandlePayments">
          <button type="button" disabled={count === 0} onClick={onSplit}
            className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
            <SplitSquareHorizontal className="size-4" /> تقسیم
          </button>
        </Can>
      </div>
    </div>
  );
}

// ── Payment sheet (cash / card / split + numpad + loyalty) ───────────────────
function Pos2PaySheet({
  tableName, amountDue, loyaltyPoints, onClose, onConfirm,
}: {
  tableName: string; amountDue: number; loyaltyPoints: number;
  onClose: () => void;
  onConfirm: (payments: Payment[], loyaltyRedeem: number) => void;
}) {
  const [method, setMethod] = useState<"cash" | "card" | "split">("card");
  const [recv, setRecv] = useState("");
  const [splitN, setSplitN] = useState(2);
  const [splitMethods, setSplitMethods] = useState<Method[]>(["Card", "Card"]);
  const [useLoyalty, setUseLoyalty] = useState(false);

  useEffect(() => {
    setSplitMethods((prev) => Array.from({ length: splitN }, (_, i) => prev[i] ?? "Cash"));
  }, [splitN]);

  const maxRedeem = Math.min(loyaltyPoints, Math.floor(amountDue / POINT_VALUE));
  const redeem = useLoyalty ? maxRedeem : 0;
  const due = Math.max(0, amountDue - redeem * POINT_VALUE);

  const received = Number(recv || 0);
  const change = received - due;
  const press = (d: string) => setRecv((r) => (r + d).replace(/^0+(?=\d)/, "").slice(0, 12));
  const backspace = () => setRecv((r) => r.slice(0, -1));
  const roundUp = (step: number) => Math.ceil(due / step) * step;

  const splitAmounts = useMemo(() => {
    const base = Math.floor(due / splitN);
    const arr = Array<number>(splitN).fill(base);
    arr[splitN - 1] += due - base * splitN;
    return arr;
  }, [due, splitN]);

  const canConfirm = method === "cash" ? received >= due || due === 0 : true;

  const confirm = () => {
    let payments: Payment[];
    if (method === "cash") payments = [{ method: "Cash", amount: due }];
    else if (method === "card") payments = [{ method: "Card", amount: due }];
    else payments = splitAmounts.map((a, i) => ({ method: splitMethods[i] ?? "Cash", amount: a }));
    onConfirm(payments.filter((p) => p.amount > 0), redeem);
  };

  // Card listed first (most common in Iran); Cash stays the pre-selected default.
  const TABS = [
    { id: "card", name: "کارت", icon: CreditCard },
    { id: "cash", name: "نقدی", icon: Banknote },
    { id: "split", name: "تقسیم", icon: SplitSquareHorizontal },
  ] as const;

  return (
    <div dir="rtl" className="fixed inset-0 z-[60] flex items-stretch justify-center sm:items-center sm:p-4">
      <div className="absolute inset-0 bg-black/50" onClick={onClose} />
      <div className="relative flex max-h-svh w-full flex-col bg-card shadow-2xl sm:max-w-md sm:rounded-2xl">
        <div className="flex items-center justify-between border-b border-border px-4 py-3">
          <div>
            <p className="text-xs text-muted-foreground">پرداخت — {tableName}</p>
            <p className="text-xl font-extrabold text-primary">
              {fmt(due)} تومان
              {redeem > 0 && <span className="ms-2 text-sm font-medium text-muted-foreground line-through">{fmt(amountDue)}</span>}
            </p>
          </div>
          <button type="button" onClick={onClose} className="rounded-lg p-2 hover:bg-accent" aria-label="بستن">
            <X className="size-5" />
          </button>
        </div>

        <div className="grid grid-cols-3 gap-2 p-3">
          {TABS.map((t) => (
            <button
              key={t.id}
              type="button"
              onClick={() => setMethod(t.id)}
              className={cn(
                "flex min-h-[52px] cursor-pointer flex-col items-center justify-center gap-1 rounded-xl border-2 text-sm font-bold transition-colors",
                method === t.id ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:bg-accent",
              )}
            >
              <t.icon className="size-5" /> {t.name}
            </button>
          ))}
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto px-3 pb-2">
          {loyaltyPoints > 0 && (
            <button
              type="button"
              onClick={() => setUseLoyalty((v) => !v)}
              className={cn(
                "mb-3 flex w-full items-center justify-between rounded-xl border-2 px-3 py-2.5 text-start transition-colors",
                useLoyalty ? "border-primary bg-primary/10" : "border-border hover:bg-accent",
              )}
            >
              <span className="flex items-center gap-2 text-sm font-medium">
                <Sparkles className={cn("size-4", useLoyalty ? "text-primary" : "text-muted-foreground")} />
                امتیاز وفاداری ({fmt(loyaltyPoints)})
                <span className="text-xs text-muted-foreground">هر امتیاز ۱۰۰ تومان</span>
              </span>
              <span className={cn("text-sm font-bold", useLoyalty ? "text-primary" : "text-muted-foreground")}>
                {useLoyalty ? `−${fmt(redeem * POINT_VALUE)}` : "استفاده"}
              </span>
            </button>
          )}

          {method === "cash" && (
            <div className="space-y-3">
              <div className="rounded-xl bg-muted/60 p-3">
                <div className="flex items-center justify-between text-sm text-muted-foreground">
                  <span>دریافتی</span>
                  <span className="text-lg font-extrabold text-foreground">{fmt(received)} تومان</span>
                </div>
                <div className="mt-1 flex items-center justify-between text-sm">
                  <span className="text-muted-foreground">{change >= 0 ? "باقی‌مانده (بازگشت)" : "کسری پرداخت"}</span>
                  <span className={cn("text-lg font-extrabold", change >= 0 ? "text-emerald-600" : "text-red-500")}>
                    {fmt(Math.abs(change))} تومان
                  </span>
                </div>
              </div>
              <div className="flex flex-wrap gap-2">
                <Chip onClick={() => setRecv(String(due))}>مبلغ دقیق</Chip>
                <Chip onClick={() => setRecv(String(roundUp(50000)))}>{fmt(roundUp(50000))}</Chip>
                <Chip onClick={() => setRecv(String(roundUp(100000)))}>{fmt(roundUp(100000))}</Chip>
                <Chip onClick={() => setRecv(String(roundUp(500000)))}>{fmt(roundUp(500000))}</Chip>
              </div>
              <div className="grid grid-cols-3 gap-2">
                {["7", "8", "9", "4", "5", "6", "1", "2", "3"].map((d) => (
                  <Key key={d} onClick={() => press(d)}>{fmt(Number(d))}</Key>
                ))}
                <Key onClick={() => press("000")}>۰۰۰</Key>
                <Key onClick={() => press("0")}>۰</Key>
                <Key onClick={backspace} aria-label="حذف"><Delete className="size-5" /></Key>
              </div>
            </div>
          )}

          {method === "card" && (
            <div className="flex flex-col items-center gap-3 py-6 text-center">
              <div className="flex size-16 items-center justify-center rounded-2xl bg-primary/10 text-primary">
                <CreditCard className="size-8" />
              </div>
              <p className="font-medium">مبلغ به دستگاه کارت‌خوان ارسال می‌شود</p>
              <p className="text-2xl font-extrabold text-primary">{fmt(due)} تومان</p>
              <p className="text-sm text-muted-foreground">پس از تأیید تراکنش، دکمهٔ زیر را بزنید</p>
            </div>
          )}

          {method === "split" && (
            <div className="space-y-3">
              <div className="flex items-center justify-between">
                <span className="text-sm font-medium text-muted-foreground">تعداد نفرات</span>
                <div className="flex items-center gap-2">
                  {[2, 3, 4, 5, 6].map((n) => (
                    <button
                      key={n}
                      type="button"
                      onClick={() => setSplitN(n)}
                      className={cn(
                        "size-11 cursor-pointer rounded-xl border-2 font-bold transition-colors",
                        splitN === n ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:bg-accent",
                      )}
                    >
                      {fmt(n)}
                    </button>
                  ))}
                </div>
              </div>
              <ul className="space-y-2">
                {splitAmounts.map((amt, i) => (
                  <li key={i} className="flex items-center justify-between gap-2 rounded-xl border border-border/70 px-3 py-2">
                    <span className="flex items-center gap-2 font-medium">
                      <span className="flex size-7 items-center justify-center rounded-full bg-primary/10 text-sm font-bold text-primary">{fmt(i + 1)}</span>
                      {fmt(amt)} تومان
                    </span>
                    <div className="flex gap-1 rounded-lg border border-border p-0.5">
                      {(["Cash", "Card"] as const).map((m) => (
                        <button
                          key={m}
                          type="button"
                          onClick={() => setSplitMethods((prev) => prev.map((x, j) => (j === i ? m : x)))}
                          className={cn(
                            "rounded-md px-2.5 py-1 text-xs font-bold transition-colors",
                            (splitMethods[i] ?? "Cash") === m ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-accent",
                          )}
                        >
                          {m === "Cash" ? "نقدی" : "کارت"}
                        </button>
                      ))}
                    </div>
                  </li>
                ))}
              </ul>
            </div>
          )}
        </div>

        <div className="border-t border-border p-3">
          <button
            type="button"
            disabled={!canConfirm}
            onClick={confirm}
            className="flex min-h-[56px] w-full items-center justify-center gap-2 rounded-xl bg-primary text-lg font-extrabold text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-40 active:scale-[0.99]"
          >
            <Check className="size-6" />
            {method === "cash" && change > 0 && received > 0
              ? `تأیید — بازگشت ${fmt(change)}`
              : `تأیید پرداخت ${fmt(due)} تومان`}
          </button>
        </div>
      </div>
    </div>
  );
}

function BusyOverlay() {
  return (
    <div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/20">
      <div className="flex items-center gap-2 rounded-xl bg-card px-4 py-3 shadow-lg">
        <Loader2 className="size-5 animate-spin text-primary" />
        <span className="font-medium">در حال پردازش…</span>
      </div>
    </div>
  );
}

function Key({ children, onClick, ...rest }: { children: React.ReactNode; onClick: () => void } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="flex min-h-[56px] cursor-pointer items-center justify-center rounded-xl bg-muted text-xl font-bold transition-colors hover:bg-accent active:scale-95"
      {...rest}
    >
      {children}
    </button>
  );
}

function Chip({ children, onClick }: { children: React.ReactNode; onClick: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="cursor-pointer rounded-full border border-border bg-background px-3 py-2 text-sm font-medium transition-colors hover:border-primary hover:text-primary active:scale-95"
    >
      {children}
    </button>
  );
}

function Row({ label, value, accent }: { label: string; value: string; accent?: boolean }) {
  return (
    <div className={cn("flex items-center justify-between", accent ? "text-emerald-600" : "text-muted-foreground")}>
      <span>{label}</span>
      <span>{value}</span>
    </div>
  );
}
