﻿"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter, useSearchParams, usePathname } from "next/navigation";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslations, useLocale } from "next-intl";
import { Minus, Plus, Search, Trash2, Video, X } from "lucide-react";
import { apiGet, apiPatch, apiPost, ApiClientError } from "@/lib/api/client";
import type {
  MenuCategory,
  MenuItem,
  Order,
  Table,
  TableBoardItem,
  QueueTicket,
} from "@/lib/api/types";
import { useAuthStore } from "@/lib/stores/auth.store";
import { useCafeSettings } from "@/lib/hooks/use-cafe-settings";
import { useCartStore, type CartItem } from "@/lib/stores/cart.store";
import { formatCurrency, formatNumber } from "@/lib/format";
import { formatOrderNumber } from "@/lib/order-number";
import { iranMobileForApi } from "@/lib/phone";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useIsRtl } from "@/lib/use-is-rtl";
import { useBranchStore } from "@/lib/stores/branch.store";
import { getOrCreateTerminalId } from "@/lib/terminal";
import { MenuItemLabels } from "@/components/menu/menu-item-labels";
import { CategoryVisual } from "@/components/menu/category-visual";
import { getMenuPrimaryName, menuItemMatchesSearch } from "@/lib/menu-display";
import { Input } from "@/components/ui/input";
import { LabeledField } from "@/components/ui/labeled-field";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { MenuItemMedia } from "@/components/menu/menu-item-media";
import {
  buildCategoryNameMap,
  inferMenuItemKind,
} from "@/lib/menu-item-image";
import { PosPayPanel } from "@/components/pos/pos-pay-panel";
import { PosTableBoard } from "@/components/pos/pos-table-board";
import { PosCustomerPicker } from "@/components/pos/pos-customer-picker";
import { PosSlipModal, type KitchenSlipLine } from "@/components/pos/pos-slip-modal";
import { PosQueueBar } from "@/components/pos/pos-queue-bar";
import {
  branchMenuItemToMenuItem,
  getBranchMenu,
} from "@/lib/api/branch-menu";
import { requestPosPayment, posDeviceErrorMessage } from "@/lib/api/pos-device";
import { orderAmountDue, submitOrderToApi, isLocalOrder } from "@/lib/pos/submit-order";
import { useSyncQueueStore } from "@/lib/stores/sync-queue.store";
import { useConfirm } from "@/components/providers/confirm-provider";

const TAX_RATE = 0.09;

function buildKitchenLines(
  pending: { menuItemId: string; quantity: number; notes?: string }[],
  cartItems: CartItem[]
): KitchenSlipLine[] {
  return pending.map((p) => {
    const line = cartItems.find((i) => i.menuItem.id === p.menuItemId);
    return {
      name: line?.menuItem.name ?? p.menuItemId,
      quantity: p.quantity,
      notes: p.notes,
    };
  });
}

function cartToKitchenLines(cartItems: CartItem[]): KitchenSlipLine[] {
  return cartItems
    .filter((i) => !i.isVoided && i.quantity > 0)
    .map((i) => ({
      name: i.menuItem.name,
      quantity: i.quantity,
      notes: i.notes,
    }));
}

/** Small square thumb on menu tiles: text | img (RTL-aware via flex order). */
function PosMenuThumbnail({
  imageUrl,
  kind,
  hasVideo,
}: {
  imageUrl?: string | null;
  kind: ReturnType<typeof inferMenuItemKind>;
  hasVideo?: boolean;
}) {
  return (
    <div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-md border border-border/60">
      <MenuItemMedia imageUrl={imageUrl} kind={kind} size="sm" />
      {hasVideo ? (
        <span
          className="absolute bottom-0 end-0 rounded-ss-sm bg-black/55 p-px"
          aria-hidden
        >
          <Video className="h-2.5 w-2.5 text-white" />
        </span>
      ) : null}
    </div>
  );
}

export function PosScreen() {
  const t = useTranslations("pos");
  const tQueue = useTranslations("queue");
  const tErrors = useTranslations("errors");
  const tCommon = useTranslations("common");
  const tDashboard = useTranslations("dashboard");
  const locale = useLocale();
  const isRtl = useIsRtl();
  const numberLocale = locale === "en" ? "en-US" : "fa-IR";
  const cafeId = useAuthStore((s) => s.user?.cafeId);
  const { data: cafeSettings } = useCafeSettings(cafeId);
  const cafeName = cafeSettings?.name ?? tDashboard("cafeName");
  const userRole = useAuthStore((s) => s.user?.role);
  const isManager = userRole === "Manager" || userRole === "Owner";
  const branchId = useBranchStore((s) => s.branchId);
  const setBranchId = useBranchStore((s) => s.setBranchId);
  const queryClient = useQueryClient();
  const confirmDialog = useConfirm();
  const isOnline = useSyncQueueStore((s) => s.isOnline);
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const reservationId = searchParams.get("reservationId");
  const reservationGuest = searchParams.get("guestName");
  const urlOrderId = searchParams.get("orderId");

  const [kitchenSlip, setKitchenSlip] = useState<{
    lines: KitchenSlipLine[];
    orderId?: string;
    tableNumber?: string | number | null;
    guestName?: string | null;
  } | null>(null);
  const [posMode, setPosMode] = useState<"order" | "pay">("order");

  const {
    items,
    addItem,
    removeItem,
    updateQty,
    couponCode,
    appliedCoupon,
    setCouponCode,
    setAppliedCoupon,
    clearCoupon,
    tableId,
    setTableId,
    activeOrderId,
    activeOrderDisplayNumber,
    setActiveOrderId,
    customerId,
    guestName,
    setGuestName,
    guestPhone,
    setGuestPhone,
    setCustomer,
    clearCustomer,
    hydrateFromOrder,
    getPendingLines,
    clearCart,
    clearSession,
    subtotal,
  } = useCartStore();

  const syncUrl = useCallback(
    (tid: string | null, oid: string | null) => {
      const params = new URLSearchParams(searchParams.toString());
      if (tid) params.set("tableId", tid);
      else params.delete("tableId");
      if (oid) params.set("orderId", oid);
      else params.delete("orderId");
      const qs = params.toString();
      router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
    },
    [pathname, router, searchParams]
  );

  useEffect(() => {
    const tid = searchParams.get("tableId");
    if (tid) setTableId(tid);
  }, [searchParams, setTableId]);

  useEffect(() => {
    if (urlOrderId) setActiveOrderId(urlOrderId);
  }, [urlOrderId, setActiveOrderId]);

  useEffect(() => {
    if (reservationGuest) setGuestName(reservationGuest);
  }, [reservationGuest, setGuestName]);

  useEffect(() => {
    if (!cafeId) return;
    apiPost(`/api/cafes/${cafeId}/terminals/register`, {
      terminalId: getOrCreateTerminalId(),
    }).catch(() => undefined);
  }, [cafeId]);

  const [selectedCategory, setSelectedCategory] = useState<string | "all">("all");
  const [itemSearch, setItemSearch] = useState("");
  const [orderMessage, setOrderMessage] = useState<string | null>(null);
  const [couponMessage, setCouponMessage] = useState<{
    type: "success" | "error";
    text: string;
  } | null>(null);
  const [showTransferPicker, setShowTransferPicker] = useState(false);

  const { data: branches = [] } = useQuery({
    queryKey: ["branches", cafeId],
    queryFn: () =>
      apiGet<{ id: string; name: string }[]>(`/api/cafes/${cafeId}/branches`),
    enabled: !!cafeId,
  });

  useEffect(() => {
    if (branches.length === 0) return;
    const valid = branchId && branches.some((b) => b.id === branchId);
    if (!valid) setBranchId(branches[0]!.id);
  }, [branches, branchId, setBranchId]);

  const orderBranchId = useMemo(() => {
    if (branches.length === 0) return null;
    if (branchId && branches.some((b) => b.id === branchId)) return branchId;
    return branches[0]?.id ?? null;
  }, [branchId, branches]);

  const { data: categories, isLoading: loadingCategories } = useQuery({
    queryKey: ["menu-categories", cafeId],
    queryFn: () => apiGet<MenuCategory[]>(`/api/cafes/${cafeId}/menu/categories`),
    enabled: !!cafeId,
  });

  const { data: globalMenuItems, isLoading: loadingGlobalItems } = useQuery({
    queryKey: ["menu-items", cafeId, selectedCategory],
    queryFn: () => {
      const qs =
        selectedCategory !== "all" ? `?categoryId=${selectedCategory}` : "";
      return apiGet<MenuItem[]>(`/api/cafes/${cafeId}/menu/items${qs}`);
    },
    enabled: !!cafeId && !orderBranchId,
  });

  const { data: branchMenuRows, isLoading: loadingBranchMenu } = useQuery({
    queryKey: ["branch-menu", cafeId, orderBranchId, selectedCategory],
    queryFn: () => getBranchMenu(cafeId!, orderBranchId!),
    enabled: !!cafeId && !!orderBranchId,
  });

  const menuItems = useMemo(() => {
    if (orderBranchId && branchMenuRows) {
      const mapped = branchMenuRows.map(branchMenuItemToMenuItem);
      if (selectedCategory === "all") return mapped;
      return mapped.filter((i) => i.categoryId === selectedCategory);
    }
    return globalMenuItems;
  }, [orderBranchId, branchMenuRows, globalMenuItems, selectedCategory]);

  const loadingItems = orderBranchId ? loadingBranchMenu : loadingGlobalItems;

  const { data: allMenuItems, isLoading: loadingAllCatalog } = useQuery({
    queryKey: ["branch-menu-all", cafeId, orderBranchId],
    queryFn: async () => {
      if (orderBranchId && cafeId) {
        const rows = await getBranchMenu(cafeId, orderBranchId);
        return rows.map(branchMenuItemToMenuItem);
      }
      return apiGet<MenuItem[]>(`/api/cafes/${cafeId}/menu/items`);
    },
    enabled: !!cafeId,
  });

  const menuById = useMemo(() => {
    const map = new Map<string, MenuItem>();
    for (const m of allMenuItems ?? menuItems ?? []) {
      map.set(m.id, m);
    }
    return map;
  }, [allMenuItems, menuItems]);

  useEffect(() => {
    if (!cafeId || !urlOrderId || menuById.size === 0) return;
    if (activeOrderId === urlOrderId && items.length > 0) return;
    apiGet<Order>(`/api/cafes/${cafeId}/orders/${urlOrderId}`)
      .then((order) => {
        hydrateFromOrder(order, menuById);
        if (order.tableId) setTableId(order.tableId);
      })
      .catch(() => undefined);
  }, [
    cafeId,
    urlOrderId,
    menuById,
    activeOrderId,
    activeOrderDisplayNumber,
    items.length,
    hydrateFromOrder,
    setTableId,
  ]);

  const sessionPatchRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    if (!cafeId || !activeOrderId) return;
    if (sessionPatchRef.current) clearTimeout(sessionPatchRef.current);
    sessionPatchRef.current = setTimeout(() => {
      apiPatch(`/api/cafes/${cafeId}/orders/${activeOrderId}/session`, {
        guestName: guestName.trim() || null,
        guestPhone: iranMobileForApi(guestPhone) ?? null,
        customerId: customerId ?? null,
      }).catch(() => undefined);
    }, 600);
    return () => {
      if (sessionPatchRef.current) clearTimeout(sessionPatchRef.current);
    };
  }, [guestName, guestPhone, customerId, activeOrderId, cafeId]);

  const handleTableSelect = useCallback(
    (table: TableBoardItem, activeOrder: Order | null) => {
      if (activeOrder) {
        setTableId(table.id);
        hydrateFromOrder(activeOrder, menuById);
        syncUrl(table.id, activeOrder.id);
        setOrderMessage(t("sessionActive"));
        return;
      }

      const hadOpenSession = !!useCartStore.getState().activeOrderId;
      if (hadOpenSession) {
        clearSession();
      } else {
        setActiveOrderId(null);
      }

      setTableId(table.id);
      syncUrl(table.id, null);
      setOrderMessage(null);
    },
    [
      setTableId,
      hydrateFromOrder,
      menuById,
      syncUrl,
      setActiveOrderId,
    activeOrderDisplayNumber,
      clearSession,
      t,
    ]
  );

  const tablesQuery = orderBranchId
    ? `?branchId=${encodeURIComponent(orderBranchId)}`
    : "";

  const { data: tables } = useQuery({
    queryKey: ["tables", cafeId, orderBranchId],
    queryFn: () => apiGet<Table[]>(`/api/cafes/${cafeId}/tables${tablesQuery}`),
    enabled: !!cafeId,
  });

  const { data: boardTables = [] } = useQuery({
    queryKey: ["tables-board", cafeId, orderBranchId, "transfer"],
    queryFn: () =>
      apiGet<TableBoardItem[]>(
        `/api/cafes/${cafeId}/tables/board${tablesQuery}`
      ),
    enabled: !!cafeId && showTransferPicker,
  });

  const voidItemMutation = useMutation({
    mutationFn: async (orderItemId: string) => {
      if (!cafeId || !activeOrderId) throw new Error("no session");
      return apiPatch<Order>(
        `/api/cafes/${cafeId}/orders/${activeOrderId}/items/${orderItemId}/void`,
        {}
      );
    },
    onSuccess: async (order) => {
      hydrateFromOrder(order, menuById);
      queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
    },
    onError: () => setOrderMessage(t("voidError")),
  });

  const transferTableMutation = useMutation({
    mutationFn: async (targetTableId: string) => {
      if (!cafeId || !activeOrderId) throw new Error("no session");
      return apiPost<Order>(`/api/cafes/${cafeId}/orders/${activeOrderId}/transfer`, {
        targetTableId,
      });
    },
    onSuccess: async (order) => {
      setShowTransferPicker(false);
      hydrateFromOrder(order, menuById);
      if (order.tableId) setTableId(order.tableId);
      syncUrl(order.tableId ?? null, order.id);
      setOrderMessage(t("transferSuccess"));
      queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
    },
    onError: (err: Error) => {
      const code = err instanceof ApiClientError ? err.code : "";
      if (code === "TABLE_OCCUPIED") setOrderMessage(t("tableOccupied"));
      else if (code === "TABLE_CLEANING") setOrderMessage(t("tableNotAvailable"));
      else setOrderMessage(t("transferError"));
    },
  });

  const handleVoidItem = async (orderItemId: string) => {
    const ok = await confirmDialog({
      description: t("confirmVoid"),
      variant: "destructive",
      confirmLabel: tCommon("confirm"),
    });
    if (!ok) return;
    voidItemMutation.mutate(orderItemId);
  };

  const freeTransferTables = boardTables.filter(
    (tbl) => tbl.status === "Free" && tbl.id !== tableId
  );

  const itemSearchQuery = itemSearch.trim();
  const isSearchingItems = itemSearchQuery.length > 0;

  const catalogForSearch = useMemo(
    () => allMenuItems ?? menuItems ?? [],
    [allMenuItems, menuItems]
  );

  const filteredItems = useMemo(() => {
    const base = isSearchingItems ? catalogForSearch : (menuItems ?? []);
    return base.filter((i) => {
      if (!i.isAvailable) return false;
      if (!isSearchingItems) return true;
      return menuItemMatchesSearch(i, itemSearchQuery, locale);
    });
  }, [
    catalogForSearch,
    menuItems,
    isSearchingItems,
    itemSearchQuery,
    locale,
  ]);

  const showItemsLoading = isSearchingItems ? loadingAllCatalog : loadingItems;

  const categoryNameById = useMemo(
    () => buildCategoryNameMap(categories ?? []),
    [categories]
  );

  const itemVisualKind = (item: MenuItem) =>
    inferMenuItemKind(item.categoryId, categoryNameById.get(item.categoryId));

  const handleSelectMenuItem = (item: MenuItem) => {
    addItem(item);
  };

  const sub = subtotal();
  const discount = appliedCoupon?.discountAmount ?? 0;
  const taxable = Math.max(0, sub - discount);
  const tax = Math.round(taxable * TAX_RATE);
  const total = taxable + tax;

  const couponErrorKey = (code: string) => {
    const map: Record<string, string> = {
      COUPON_NOT_FOUND: "couponInvalid",
      COUPON_INACTIVE: "couponInvalid",
      COUPON_EXPIRED: "couponExpired",
      COUPON_NOT_STARTED: "couponNotStarted",
      COUPON_LIMIT_REACHED: "couponLimitReached",
      COUPON_MIN_ORDER: "couponMinOrder",
      CART_EMPTY: "couponCartEmpty",
      COUPON_REQUIRED: "couponRequired",
      COUPON_NO_DISCOUNT: "couponInvalid",
    };
    return map[code] ?? "couponInvalid";
  };

  const validateCoupon = useMutation({
    mutationFn: async () => {
      if (!cafeId) throw new Error("no cafe");
      return apiPost<{
        couponId: string;
        code: string;
        discountAmount: number;
      }>(`/api/cafes/${cafeId}/coupons/validate`, {
        code: couponCode.trim(),
        subtotal: subtotal(),
      });
    },
    onSuccess: (data) => {
      setAppliedCoupon({
        id: data.couponId,
        code: data.code,
        discountAmount: data.discountAmount,
      });
      setCouponMessage({
        type: "success",
        text: t("couponApplied", {
          code: data.code,
          amount: formatCurrency(data.discountAmount, numberLocale),
        }),
      });
    },
    onError: (err: Error) => {
      setAppliedCoupon(null);
      const code = err instanceof ApiClientError ? err.code : "COUPON_NOT_FOUND";
      setCouponMessage({ type: "error", text: t(couponErrorKey(code)) });
    },
  });

  const openKitchenSlip = useCallback(() => {
    const pending = getPendingLines();
    const lines =
      pending.length > 0
        ? buildKitchenLines(pending, items)
        : cartToKitchenLines(items);
    if (lines.length === 0) return;
    setKitchenSlip({
      lines,
      orderId: activeOrderId ?? undefined,
      tableNumber: tables?.find((tbl) => tbl.id === tableId)?.number ?? null,
      guestName: guestName.trim() || null,
    });
  }, [getPendingLines, items, activeOrderId, tables, tableId, guestName]);

  const submitOrder = useMutation({
    mutationFn: async () => {
      if (!cafeId || items.length === 0) throw new Error("empty");
      const cart = useCartStore.getState();
      const pending = cart.getPendingLines();
      if (pending.length === 0) throw new Error("nothing pending");
      const kitchenLines = buildKitchenLines(pending, cart.items);
      const order = await submitOrderToApi({
        cafeId,
        orderBranchId: orderBranchId ?? undefined,
        cart,
        reservationId,
        cartItems: cart.items,
      });
      return { order, kitchenLines };
    },
    onMutate: () => ({ hadSession: !!useCartStore.getState().activeOrderId }),
    onSuccess: ({ order, kitchenLines }, _, context) => {
      hydrateFromOrder(order, menuById);
      syncUrl(order.tableId ?? tableId, order.id);
      setCouponMessage(null);
      if (kitchenLines.length > 0) {
        setKitchenSlip({
          lines: kitchenLines,
          orderId: order.id,
          tableNumber: order.tableNumber ?? null,
          guestName: order.guestName ?? (guestName.trim() || null),
        });
      }
      const baseMsg = context?.hadSession ? t("addToOrder") : t("orderPlaced");
      setOrderMessage(baseMsg);
      void apiPost<QueueTicket>(`/api/cafes/${cafeId}/queue/next`, {
        branchId: orderBranchId,
        customerLabel:
          order.guestName ?? (guestName.trim() || undefined),
        orderId: order.id,
      })
        .then((ticket) => {
          setOrderMessage(
            `${baseMsg} Â· ${t("queueNumber", { number: ticket.number })}`
          );
          queryClient.invalidateQueries({ queryKey: ["queue-today"] });
        })
        .catch(() => undefined);
      queryClient.invalidateQueries({ queryKey: ["orders-live", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
      if (reservationId) {
        queryClient.invalidateQueries({ queryKey: ["reservations", cafeId] });
      }
    },
    onError: (err: Error) => {
      if (err instanceof ApiClientError) {
        if (err.code === "ORDER_NOT_FOUND" || err.code === "ORDER_NOT_OPEN") {
          setActiveOrderId(null);
          syncUrl(tableId, null);
        }
        const key =
          err.code === "TABLE_NOT_AVAILABLE"
            ? "tableNotAvailable"
            : err.code === "TABLE_OCCUPIED"
              ? "tableOccupied"
              : err.code === "PLAN_LIMIT_REACHED"
                ? "planLimit"
                : err.code === "INVALID_ORDER"
                  ? "orderInvalid"
                  : err.code === "ORDER_NOT_OPEN"
                    ? "orderNotOpen"
                    : err.code === "ORDER_NOT_FOUND"
                      ? "orderNotOpen"
                      : err.code === "VALIDATION_ERROR"
                        ? "orderValidation"
                        : "orderError";
        setOrderMessage(
          err.code === "VALIDATION_ERROR"
            ? err.message
            : key === "planLimit"
              ? tErrors("planLimit")
              : t(key)
        );
        return;
      }
      if (err.message === "nothing pending") {
        setOrderMessage(t("nothingPending"));
        return;
      }
      setOrderMessage(t("orderError"));
    },
  });

  const submitOrderAndPay = useMutation({
    mutationFn: async () => {
      if (!cafeId || items.length === 0) throw new Error("empty");
      const cart = useCartStore.getState();
      const pending = cart.getPendingLines();
      if (pending.length === 0) throw new Error("nothing pending");
      const kitchenLines = buildKitchenLines(pending, cart.items);
      const order = await submitOrderToApi({
        cafeId,
        orderBranchId: orderBranchId ?? undefined,
        cart,
        reservationId,
        cartItems: cart.items,
      });
      const due = orderAmountDue(order);
      // Can't process payment for a local (offline) order
      if (isLocalOrder(order.id)) return { order, kitchenLines };
      const payBranchId = order.branchId ?? orderBranchId;
      if (due > 0) {
        if (!payBranchId) throw new Error("no branch");
        await requestPosPayment(cafeId, payBranchId, order.id, due);
        await apiPost(`/api/cafes/${cafeId}/orders/${order.id}/payments`, {
          payments: [{ method: "Card", amount: due }],
        });
      }
      return { order, kitchenLines };
    },
    onMutate: () => ({ hadSession: !!useCartStore.getState().activeOrderId }),
    onSuccess: ({ order, kitchenLines }, _, context) => {
      hydrateFromOrder(order, menuById);
      syncUrl(order.tableId ?? tableId, order.id);
      setCouponMessage(null);
      if (kitchenLines.length > 0) {
        setKitchenSlip({
          lines: kitchenLines,
          orderId: order.id,
          tableNumber: order.tableNumber ?? null,
          guestName: order.guestName ?? (guestName.trim() || null),
        });
      }
      const baseMsg = context?.hadSession ? t("orderPaidAdd") : t("orderPaidNew");
      setOrderMessage(baseMsg);
      queryClient.invalidateQueries({ queryKey: ["orders-live", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
      queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
      if (reservationId) {
        queryClient.invalidateQueries({ queryKey: ["reservations", cafeId] });
      }
    },
    onError: (err: Error) => {
      if (err instanceof ApiClientError) {
        if (
          err.code === "POS_DEVICE_CONNECTION_FAILED" ||
          err.code === "POS_DEVICE_TIMEOUT" ||
          err.code === "POS_DEVICE_REJECTED" ||
          err.code.startsWith("POS_DEVICE")
        ) {
          setOrderMessage(posDeviceErrorMessage(err, t));
          return;
        }
        if (err.code === "ORDER_NOT_FOUND" || err.code === "ORDER_NOT_OPEN") {
          setActiveOrderId(null);
          syncUrl(tableId, null);
        }
        const key =
          err.code === "TABLE_NOT_AVAILABLE"
            ? "tableNotAvailable"
            : err.code === "TABLE_OCCUPIED"
              ? "tableOccupied"
              : err.code === "PLAN_LIMIT_REACHED"
                ? "planLimit"
                : err.code === "INVALID_ORDER"
                  ? "orderInvalid"
                  : err.code === "ORDER_NOT_OPEN"
                    ? "orderNotOpen"
                    : err.code === "ORDER_NOT_FOUND"
                      ? "orderNotOpen"
                      : err.code === "VALIDATION_ERROR"
                        ? "orderValidation"
                        : "orderError";
        setOrderMessage(
          err.code === "VALIDATION_ERROR"
            ? err.message
            : key === "planLimit"
              ? tErrors("planLimit")
              : t(key)
        );
        return;
      }
      if (err.message === "nothing pending") {
        setOrderMessage(t("nothingPending"));
        return;
      }
      if (err.message === "no branch") {
        setOrderMessage(t("posDeviceNoBranch"));
        return;
      }
      setOrderMessage(t("payError"));
    },
  });

  const pendingCount = getPendingLines().length;
  const isOrderBusy = submitOrder.isPending || submitOrderAndPay.isPending;
  const canSubmitOrder =
    pendingCount > 0 &&
    (!!tableId || !!customerId || guestName.trim().length > 0);

  if (!cafeId) return null;

  return (
    <div
      className="flex h-full min-h-0 w-full flex-col gap-3 overflow-hidden"
      dir={isRtl ? "rtl" : "ltr"}
    >
      <div className="flex shrink-0 gap-2">
        <Button
          size="sm"
          variant={posMode === "order" ? "default" : "outline"}
          onClick={() => setPosMode("order")}
        >
          {t("modeOrder")}
        </Button>
        <Button
          size="sm"
          variant={posMode === "pay" ? "default" : "outline"}
          onClick={() => setPosMode("pay")}
        >
          {t("modePay")}
        </Button>
      </div>

      {posMode === "pay" ? (
        <PosPayPanel
          cafeId={cafeId}
          numberLocale={numberLocale}
          branchId={orderBranchId}
        />
      ) : (
    <div
      className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden"
    >
      {cafeId ? <PosQueueBar cafeId={cafeId} branchId={orderBranchId} /> : null}
    <div
      className="flex min-h-0 flex-1 gap-4 overflow-hidden"
    >
      {/* Menu panel */}
      <div className="flex min-h-0 min-w-0 flex-1 flex-col gap-3 overflow-hidden">
        {cafeId ? (
          <PosTableBoard
            cafeId={cafeId}
            numberLocale={numberLocale}
            selectedTableId={tableId}
            branchId={orderBranchId}
            onSelectTable={handleTableSelect}
          />
        ) : null}
        {activeOrderId ? (
          <p className="text-xs text-[#0F6E56]">
            {t("sessionActive")} · {t("order")}{" "}
            {activeOrderDisplayNumber
              ? String(activeOrderDisplayNumber)
              : formatOrderNumber({ id: activeOrderId })}
          </p>
        ) : null}
        {reservationId && reservationGuest ? (
          <div className="shrink-0 rounded-lg border border-[#0F6E56]/30 bg-[#E1F5EE] px-3 py-2 text-sm text-[#0F6E56]">
            {t("reservationBanner", { name: reservationGuest })}
          </div>
        ) : null}
        <div className="relative shrink-0">
          <Search
            className="pointer-events-none absolute top-1/2 size-4 -translate-y-1/2 text-muted-foreground start-3"
            aria-hidden
          />
          <Input
            type="search"
            value={itemSearch}
            onChange={(e) => setItemSearch(e.target.value)}
            placeholder={t("searchItemsPlaceholder")}
            aria-label={t("searchItems")}
            className="h-9 ps-9 pe-9"
          />
          {itemSearch ? (
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="absolute top-1/2 size-7 -translate-y-1/2 end-1"
              onClick={() => setItemSearch("")}
              aria-label={tCommon("cancel")}
            >
              <X className="size-4" />
            </Button>
          ) : null}
        </div>
        <div className="flex shrink-0 flex-wrap items-end gap-2 overflow-x-auto pb-0.5">
          <Button
            size="sm"
            variant={selectedCategory === "all" ? "default" : "outline"}
            onClick={() => setSelectedCategory("all")}
          >
            {t("allCategories")}
          </Button>
          {loadingCategories
            ? Array.from({ length: 3 }).map((_, i) => (
                <Skeleton key={i} className="h-9 w-20" />
              ))
            : categories?.map((c) => (
                <Button
                  key={c.id}
                  size="sm"
                  variant={selectedCategory === c.id ? "default" : "outline"}
                  onClick={() => setSelectedCategory(c.id)}
                  className="gap-1.5"
                >
                  <CategoryVisual
                    icon={c.icon}
                    iconPresetId={c.iconPresetId}
                    iconStyle={c.iconStyle}
                    imageUrl={c.imageUrl}
                    size="xs"
                  />
                  {getMenuPrimaryName(c, locale)}
                </Button>
              ))}
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
          <div className="grid grid-cols-2 gap-2 content-start lg:grid-cols-3 xl:grid-cols-4">
          {showItemsLoading
            ? Array.from({ length: 8 }).map((_, i) => (
                <Skeleton key={i} className="h-[3.75rem] rounded-lg" />
              ))
            : filteredItems.length === 0 && isSearchingItems
              ? (
                  <p className="col-span-full py-8 text-center text-sm text-muted-foreground">
                    {t("searchNoResults")}
                  </p>
                )
              : filteredItems.map((item) => (
                <button
                  key={item.id}
                  type="button"
                  onClick={() => handleSelectMenuItem(item)}
                  className="flex items-center gap-2.5 rounded-lg border border-border bg-card p-2 text-start shadow-sm transition hover:border-primary hover:shadow-md"
                >
                  <div className="min-w-0 flex-1">
                    <MenuItemLabels
                      item={item}
                      lines={1}
                      primaryClassName="text-sm"
                      secondaryClassName="text-[10px]"
                    />
                    <p className="mt-0.5 text-sm font-medium text-primary">
                      {formatCurrency(item.price, numberLocale)}
                    </p>
                  </div>
                  <PosMenuThumbnail
                    imageUrl={item.imageUrl}
                    kind={itemVisualKind(item)}
                    hasVideo={!!item.videoUrl}
                  />
                </button>
              ))}
          </div>
        </div>
      </div>

      {/* Cart sidebar */}
      <Card className="flex h-full min-h-0 w-[min(100%,20rem)] shrink-0 flex-col overflow-hidden sm:w-72 lg:w-80">
        <CardHeader className="shrink-0 space-y-1.5 p-3 pb-2">
          <div className="flex items-center justify-between gap-2">
            <CardTitle className="text-base">{t("takeOrder")}</CardTitle>
            {tableId ? (
              <span className="shrink-0 rounded-md border-2 border-primary bg-primary/10 px-2 py-0.5 text-xs font-semibold text-primary">
                {t("table")}{" "}
                {tables?.find((tbl) => tbl.id === tableId)?.number ?? "â€”"}
              </span>
            ) : (
              <span className="shrink-0 text-[10px] text-muted-foreground">
                {t("selectTableBoard")}
              </span>
            )}
          </div>
          {cafeId ? (
            <PosCustomerPicker
              compact
              cafeId={cafeId}
              guestName={guestName}
              guestPhone={guestPhone}
              customerId={customerId}
              onGuestNameChange={setGuestName}
              onGuestPhoneChange={setGuestPhone}
              onCustomerChange={setCustomer}
              onClearCustomer={clearCustomer}
            />
          ) : null}
          {activeOrderId && tableId ? (
            <Button
              type="button"
              variant="outline"
              size="sm"
              className="h-8 w-full text-xs"
              onClick={() => setShowTransferPicker(true)}
            >
              {t("transferTable")}
            </Button>
          ) : null}
        </CardHeader>

        <CardContent className="flex min-h-0 flex-1 flex-col overflow-hidden p-3 pt-0">
          <div className="min-h-0 flex-1 space-y-1.5 overflow-y-auto overscroll-contain pe-0.5">
            {items.length === 0 ? (
              <p className="text-center text-sm text-muted-foreground">{t("emptyCart")}</p>
            ) : (
              items.map((line) => (
                <div
                  key={line.orderItemId ?? line.menuItem.id}
                  className={cn(
                    "flex items-center gap-2 rounded-md border border-border p-1.5",
                    line.isVoided && "opacity-60"
                  )}
                >
                  <div className="min-w-0 flex-1">
                    <MenuItemLabels
                      item={line.menuItem}
                      lines={1}
                      primaryClassName={cn(
                        "text-xs font-medium",
                        line.isVoided && "line-through text-muted-foreground"
                      )}
                      secondaryClassName="text-[10px]"
                    />
                    <p className="mt-0.5 text-[11px] text-muted-foreground">
                      {line.isVoided ? (
                        <span>{t("voided")}</span>
                      ) : (
                        formatCurrency(
                          line.menuItem.price * line.quantity,
                          numberLocale
                        )
                      )}
                    </p>
                  </div>
                  <div
                    className="flex shrink-0 items-center gap-0.5"
                    onClick={(e) => e.stopPropagation()}
                    onKeyDown={(e) => e.stopPropagation()}
                  >
                    {isManager &&
                    line.orderItemId &&
                    !line.isVoided &&
                    activeOrderId ? (
                      <button
                        type="button"
                        className="text-[10px] text-destructive hover:underline"
                        onClick={() => handleVoidItem(line.orderItemId!)}
                        aria-label={t("voidItem")}
                      >
                        {t("void")}
                      </button>
                    ) : null}
                    {!line.isVoided ? (
                      <>
                    <Button
                      size="icon"
                      variant="outline"
                      className="h-7 w-7"
                      onClick={() =>
                        updateQty(line.menuItem.id, line.quantity - 1)
                      }
                    >
                      <Minus className="h-3 w-3" />
                    </Button>
                    <span className="w-5 text-center text-xs">
                      {formatNumber(line.quantity, numberLocale)}
                    </span>
                    <Button
                      size="icon"
                      variant="outline"
                      className="h-7 w-7"
                      onClick={() =>
                        updateQty(line.menuItem.id, line.quantity + 1)
                      }
                    >
                      <Plus className="h-3 w-3" />
                    </Button>
                    <Button
                      size="icon"
                      variant="ghost"
                      className="h-7 w-7 text-destructive"
                      onClick={() => removeItem(line.menuItem.id)}
                    >
                      <Trash2 className="h-3 w-3" />
                    </Button>
                      </>
                    ) : null}
                  </div>
                </div>
              ))
            )}
          </div>

          <div className="shrink-0 space-y-1.5 border-t border-border bg-card pt-2">
            <div className="flex flex-wrap items-end gap-1.5">
              <LabeledField label={t("couponCode")} htmlFor="pos-coupon" className="min-w-0 flex-1">
                <Input
                  id="pos-coupon"
                  value={couponCode}
                  onChange={(e) => {
                    setCouponCode(e.target.value);
                    if (couponMessage) setCouponMessage(null);
                  }}
                  onKeyDown={(e) => {
                    if (
                      e.key === "Enter" &&
                      !appliedCoupon &&
                      couponCode.trim() &&
                      !validateCoupon.isPending
                    ) {
                      e.preventDefault();
                      validateCoupon.mutate();
                    }
                  }}
                  disabled={!!appliedCoupon}
                  dir="ltr"
                  className="h-8 text-end text-sm"
                />
              </LabeledField>
              {appliedCoupon ? (
                <Button
                  variant="outline"
                  onClick={() => {
                    clearCoupon();
                    setCouponMessage({ type: "success", text: t("couponRemoved") });
                  }}
                >
                  {t("removeCoupon")}
                </Button>
              ) : (
                <Button
                  variant="outline"
                  disabled={!couponCode.trim() || items.length === 0 || validateCoupon.isPending}
                  onClick={() => validateCoupon.mutate()}
                >
                  {t("applyCoupon")}
                </Button>
              )}
            </div>
            {couponMessage ? (
              <p
                className={cn(
                  "text-center text-sm",
                  couponMessage.type === "success" ? "text-[#0F6E56]" : "text-[#A32D2D]"
                )}
              >
                {couponMessage.text}
              </p>
            ) : null}
            {appliedCoupon ? (
              <div className="flex justify-between text-sm text-[#BA7517]">
                <span>{t("couponActive", { code: appliedCoupon.code })}</span>
                <span>-{formatCurrency(discount, numberLocale)}</span>
              </div>
            ) : null}
            <div className="flex justify-between text-sm">
              <span>{t("subtotal")}</span>
              <span>{formatCurrency(sub, numberLocale)}</span>
            </div>
            {discount > 0 ? (
              <div className="flex justify-between text-sm text-[#0F6E56]">
                <span>{t("discount")}</span>
                <span>-{formatCurrency(discount, numberLocale)}</span>
              </div>
            ) : null}
            <div className="flex justify-between text-sm">
              <span>{t("tax")}</span>
              <span>{formatCurrency(tax, numberLocale)}</span>
            </div>
            <div className="flex justify-between text-sm font-bold">
              <span>{t("total")}</span>
              <span>{formatCurrency(total, numberLocale)}</span>
            </div>
            {!isOnline ? (
              <p className="rounded-md bg-amber-50 px-2 py-1 text-center text-[11px] text-amber-700 border border-amber-200">
                {t("offlineQueueNotice")}
              </p>
            ) : null}
            {orderMessage ? (
              <p className="text-center text-xs text-primary">{orderMessage}</p>
            ) : null}
            {!canSubmitOrder && items.length > 0 ? (
              <p className="text-center text-[10px] text-amber-700">{t("needTableOrName")}</p>
            ) : null}
            {items.some((line) => !line.isVoided) ? (
              <Button
                type="button"
                size="sm"
                variant="outline"
                className="w-full"
                onClick={openKitchenSlip}
              >
                {t("kitchenSlip")}
              </Button>
            ) : null}
            <div className="flex flex-col gap-2 pt-0.5">
              <Button
                size="sm"
                className="w-full"
                disabled={!canSubmitOrder || isOrderBusy}
                onClick={() => submitOrderAndPay.mutate()}
              >
                {submitOrderAndPay.isPending ? "..." : t("submitOrderAndPay")}
              </Button>
              <div className="flex gap-2">
                <Button
                  size="sm"
                  variant="outline"
                  className="flex-1"
                  disabled={!canSubmitOrder || isOrderBusy}
                  onClick={() => submitOrder.mutate()}
                >
                  {submitOrder.isPending ? "..." : t("submitOrder")}
                </Button>
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => {
                    clearSession();
                    syncUrl(null, null);
                    setOrderMessage(null);
                  }}
                >
                  {t("clearCart")}
                </Button>
              </div>
            </div>
          </div>
        </CardContent>
      </Card>
    </div>
    </div>
      )}

      {kitchenSlip ? (
        <PosSlipModal
          variant="kitchen"
          cafeName={cafeName}
          kitchenLines={kitchenSlip.lines}
          orderId={kitchenSlip.orderId}
          tableNumber={kitchenSlip.tableNumber}
          guestName={kitchenSlip.guestName}
          onClose={() => setKitchenSlip(null)}
        />
      ) : null}

      {showTransferPicker ? (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
          <div className="w-full max-w-sm rounded-xl border border-border bg-background p-4 shadow-lg">
            <p className="mb-3 text-sm font-medium">{t("selectTargetTable")}</p>
            <div className="mb-4 flex max-h-48 flex-wrap gap-2 overflow-y-auto">
              {freeTransferTables.length === 0 ? (
                <p className="text-sm text-muted-foreground">{t("noOrderOnTable")}</p>
              ) : (
                freeTransferTables.map((tbl) => (
                  <Button
                    key={tbl.id}
                    type="button"
                    variant="outline"
                    size="sm"
                    disabled={transferTableMutation.isPending}
                    onClick={() => transferTableMutation.mutate(tbl.id)}
                  >
                    {t("table")} {tbl.number}
                  </Button>
                ))
              )}
            </div>
            <Button
              type="button"
              variant="ghost"
              className="w-full"
              onClick={() => setShowTransferPicker(false)}
            >
              {tCommon("cancel")}
            </Button>
          </div>
        </div>
      ) : null}
    </div>
  );
}
