import { apiPost } from "@/lib/api/client";
import type { Order, OrderItemLine } from "@/lib/api/types";
import type { CartItem } from "@/lib/stores/cart.store";
import { iranMobileForApi } from "@/lib/phone";
import { enqueueOfflineItem, getQueueCount } from "@/lib/offline/offline-db";
import { useSyncQueueStore } from "@/lib/stores/sync-queue.store";

export type SubmitOrderCart = {
  getPendingLines: () => { menuItemId: string; quantity: number; notes?: string }[];
  activeOrderId: string | null;
  tableId: string | null;
  guestName: string;
  guestPhone: string;
  customerId: string | null;
  appliedCoupon: { id: string } | null;
};

export type SubmitOrderParams = {
  cafeId: string;
  orderBranchId: string | undefined;
  cart: SubmitOrderCart;
  reservationId: string | null;
  /** Cart items (needed to build the offline mock order) */
  cartItems?: CartItem[];
};

// ─── Offline helpers ──────────────────────────────────────────────────────────

function isNetworkError(err: unknown): boolean {
  if (err instanceof TypeError) {
    const msg = err.message.toLowerCase();
    return (
      msg.includes("failed to fetch") ||
      msg.includes("networkerror") ||
      msg.includes("load failed") ||
      msg.includes("network request failed")
    );
  }
  return false;
}

function newLocalId(): string {
  return `local_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}

/** Build a synthetic Order that keeps the POS cart functional while offline */
function buildLocalOrder(
  params: SubmitOrderParams,
  cartItems: CartItem[]
): Order {
  const pending = params.cart.getPendingLines();
  const localId = newLocalId();

  const items: OrderItemLine[] = pending.map((p) => {
    const ci = cartItems.find((c) => c.menuItem.id === p.menuItemId);
    return {
      id: newLocalId(),
      menuItemId: p.menuItemId,
      menuItemName: ci?.menuItem.name ?? p.menuItemId,
      quantity: p.quantity,
      unitPrice: ci?.menuItem.price ?? 0,
      notes: p.notes,
      isVoided: false,
    };
  });

  const subtotal = items.reduce((s, i) => s + i.unitPrice * i.quantity, 0);
  const taxTotal = Math.round(subtotal * 0.09);
  const total = subtotal + taxTotal;

  return {
    id: localId,
    cafeId: params.cafeId,
    branchId: params.orderBranchId,
    tableId: params.cart.tableId ?? undefined,
    guestName: params.cart.guestName.trim() || undefined,
    guestPhone: iranMobileForApi(params.cart.guestPhone) ?? undefined,
    customerId: params.cart.customerId ?? undefined,
    orderType: "DineIn",
    status: "Open",
    subtotal,
    taxTotal,
    discountAmount: 0,
    total,
    paidAmount: 0,
    createdAt: new Date().toISOString(),
    displayNumber: 0,
    items,
    payments: [],
  };
}

async function queueAndBuildLocalOrder(
  params: SubmitOrderParams,
  cartItems: CartItem[]
): Promise<Order> {
  const pending = params.cart.getPendingLines();
  if (pending.length === 0) throw new Error("nothing pending");

  const isAddToExisting =
    !!params.cart.activeOrderId &&
    !params.cart.activeOrderId.startsWith("local_");

  await enqueueOfflineItem({
    id: newLocalId(),
    type: isAddToExisting ? "add_items" : "create_order",
    cafeId: params.cafeId,
    targetOrderId: isAddToExisting ? params.cart.activeOrderId : null,
    payload: isAddToExisting
      ? {
          cafeId: params.cafeId,
          orderId: params.cart.activeOrderId!,
          body: { items: pending },
        }
      : {
          cafeId: params.cafeId,
          body: {
            orderType: "DineIn",
            branchId: params.orderBranchId,
            tableId: params.cart.tableId ?? undefined,
            reservationId: params.reservationId ?? undefined,
            guestName: params.cart.guestName.trim() || undefined,
            guestPhone: iranMobileForApi(params.cart.guestPhone),
            customerId: params.cart.customerId ?? undefined,
            couponId: params.cart.appliedCoupon?.id,
            items: pending,
          },
        },
    createdAt: new Date().toISOString(),
  });

  // Update global queue count
  const count = await getQueueCount();
  useSyncQueueStore.getState().setQueueCount(count);

  return buildLocalOrder(params, cartItems);
}

// ─── Main export ──────────────────────────────────────────────────────────────

export async function submitOrderToApi({
  cafeId,
  orderBranchId,
  cart,
  reservationId,
  cartItems = [],
}: SubmitOrderParams): Promise<Order> {
  const pending = cart.getPendingLines();
  if (pending.length === 0) throw new Error("nothing pending");

  const tryOnline = async (): Promise<Order> => {
    if (cart.activeOrderId && !cart.activeOrderId.startsWith("local_")) {
      return apiPost<Order>(`/api/cafes/${cafeId}/orders/${cart.activeOrderId}/items`, {
        items: pending,
      });
    }
    return apiPost<Order>(`/api/cafes/${cafeId}/orders`, {
      orderType: "DineIn",
      branchId: orderBranchId,
      tableId: cart.tableId ?? undefined,
      reservationId: reservationId ?? undefined,
      guestName: cart.guestName.trim() || undefined,
      guestPhone: iranMobileForApi(cart.guestPhone),
      customerId: cart.customerId ?? undefined,
      couponId: cart.appliedCoupon?.id,
      items: pending,
    });
  };

  // Try online first
  if (navigator.onLine) {
    try {
      return await tryOnline();
    } catch (err) {
      // If it's a network error despite onLine flag, fall through to offline path
      if (!isNetworkError(err)) throw err;
    }
  }

  // Offline path: queue and return a local mock order
  return queueAndBuildLocalOrder({ cafeId, orderBranchId, cart, reservationId, cartItems }, cartItems);
}

export function orderAmountDue(order: Order): number {
  return Math.max(0, order.total - (order.paidAmount ?? 0));
}

/** True when the order was created locally (offline) and not yet synced */
export function isLocalOrder(orderId: string | null): boolean {
  return !!orderId?.startsWith("local_");
}
