// @WHO:   vps-deploy/api/kds/src/routes/public.ts
// @WHAT:  Public API routes — menu browsing, order creation, group orders (no auth required)
// @PART:  api
// @ENTRY: publicRouter

import { Router } from 'express';
import { prisma } from '../lib/prisma';
import { getIO } from '../lib/socket';
import { deductStockForOrder } from '../lib/stock-deduction';
import { detectAnomalyAfterDeduction } from '../lib/anomaly';

export const publicRouter = Router();

// @FLOW:     MENU_FETCH
// @CALLS:    DB:outlets SELECT
// @MUTATES:  none
publicRouter.get('/outlets/:slug', async (req, res) => {
  try {
    const outlet = await prisma.outlet.findUnique({
      where: { slug: req.params.slug, isActive: true },
      select: {
        id: true,
        slug: true,
        name: true,
        description: true,
        address: true,
        phone: true,
        image: true,
      },
    });

    if (!outlet) {
      return res.status(404).json({ error: 'Outlet not found' });
    }

    res.json(outlet);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch outlet', message: err.message });
  }
});

// @FLOW:     MENU_FETCH
// @CALLS:    DB:menus + DB:categories + DB:menu_modifiers + DB:menu_modifier_options
// @MUTATES:  none
publicRouter.get('/outlets/:slug/menus', async (req, res) => {
  try {
    const outlet = await prisma.outlet.findUnique({
      where: { slug: req.params.slug, isActive: true },
      select: { id: true },
    });

    if (!outlet) {
      return res.status(404).json({ error: 'Outlet not found' });
    }

    const categories = await prisma.category.findMany({
      where: { outletId: outlet.id, isActive: true },
      orderBy: { sortOrder: 'asc' },
      include: {
        menus: {
          where: { isAvailable: true },
          orderBy: { sortOrder: 'asc' },
          include: {
            modifiers: {
              include: {
                options: {
                  orderBy: { priceAdjustment: 'asc' },
                },
              },
              orderBy: { name: 'asc' },
            },
          },
        },
      },
    });

    // Format response: categories with menus grouped
    const result = categories.map((cat) => ({
      id: cat.id,
      name: cat.name,
      icon: cat.icon,
      sortOrder: cat.sortOrder,
      menus: cat.menus.map((menu) => ({
        id: menu.id,
        name: menu.name,
        description: menu.description,
        price: menu.price,
        image: menu.image,
        modifiers: menu.modifiers.map((mod) => ({
          id: mod.id,
          name: mod.name,
          required: mod.required,
          maxSelect: mod.maxSelect,
          options: mod.options.map((opt) => ({
            id: opt.id,
            name: opt.name,
            priceAdjustment: opt.priceAdjustment,
            isDefault: opt.isDefault,
          })),
        })),
      })),
    }));

    res.json({ outletId: outlet.id, categories: result });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch menus', message: err.message });
  }
});

// @FLOW:     ORDER_CREATE
// @CALLS:    DB:outlets SELECT, DB:menus SELECT, DB:orders INSERT, DB:order_items INSERT, DB:payments INSERT
// @MUTATES:  DB:orders (INSERT), DB:order_items (INSERT), DB:payments (INSERT)
publicRouter.post('/orders', async (req, res) => {
  try {
    const { outletId, customerName, customerPhone, paymentMethod, items, notes, userId } = req.body;

    // Validate required fields
    if (!outletId || !items || !Array.isArray(items) || items.length === 0) {
      return res.status(400).json({ error: 'outletId and items[] are required' });
    }

    // Validate outlet exists
    const outlet = await prisma.outlet.findUnique({ where: { id: outletId, isActive: true } });
    if (!outlet) {
      return res.status(404).json({ error: 'Outlet not found' });
    }

    // Validate menu items and calculate totals
    let totalAmount = 0;
    const orderItemsData: any[] = [];

    for (const item of items) {
      const menu = await prisma.menu.findUnique({
        where: { id: item.menuId },
        include: { modifiers: { include: { options: true } } },
      });

      if (!menu || !menu.isAvailable) {
        return res.status(400).json({ error: `Menu item not available: ${item.menuId}` });
      }

      let itemSubtotal = menu.price * item.quantity;

      const modifierData: any[] = [];
      if (item.modifiers && Array.isArray(item.modifiers)) {
        for (const mod of item.modifiers) {
          const dbModifier = menu.modifiers.find((m) => m.id === mod.modifierId);
          if (!dbModifier) continue;

          for (const optId of mod.optionIds) {
            const dbOption = dbModifier.options.find((o) => o.id === optId);
            if (dbOption) {
              itemSubtotal += dbOption.priceAdjustment * item.quantity;
              modifierData.push({
                modifierName: dbModifier.name,
                optionName: dbOption.name,
                priceAdjustment: dbOption.priceAdjustment,
              });
            }
          }
        }
      }

      totalAmount += itemSubtotal;

      orderItemsData.push({
        menuId: menu.id,
        menuName: menu.name,
        quantity: item.quantity,
        unitPrice: menu.price,
        subtotal: itemSubtotal,
        notes: item.notes,
        modifiers: modifierData,
      });
    }

    // Generate order number
    const orderCount = await prisma.order.count({ where: { outletId } });
    const orderNumber = `KAW-${String(orderCount + 1).padStart(5, '0')}`;

    // Create order with items and payment
    const order = await prisma.order.create({
      data: {
        orderNumber,
        outletId,
        userId: userId || null,
        customerName,
        customerPhone,
        paymentMethod: paymentMethod || 'CASH',
        totalAmount,
        notes,
        status: 'RECEIVED',
        items: {
          create: orderItemsData.map((item) => ({
            menuId: item.menuId,
            menuName: item.menuName,
            quantity: item.quantity,
            unitPrice: item.unitPrice,
            subtotal: item.subtotal,
            notes: item.notes,
            modifiers: {
              create: item.modifiers,
            },
          })),
        },
        payment: {
          create: {
            method: paymentMethod || 'CASH',
            amount: totalAmount,
            status: paymentMethod === 'CASH' ? 'pending' : 'pending',
          },
        },
      },
      include: {
        items: { include: { modifiers: true } },
        payment: true,
      },
    });

    res.status(201).json(order);

    // Emit Socket.IO event for new order
    const io = getIO();
    if (io) {
      const outletId = order.outletId;
      io.to(`outlet:${outletId}:orders`).emit('order:new', order);
      io.to(`outlet:${outletId}:kds`).emit('order:new', order);
      io.to(`outlet:${outletId}:cashier`).emit('order:new', order);
      console.log(`[SOCKET] Emitted order:new for order ${order.orderNumber}`);
    }

    // @FLOW:   STOCK_DEDUCT_AFTER_ORDER
    // Async stock deduction — does not block order creation response
    // Hybrid: Recipe → Regression → Skip (cold start)
    deductStockForOrder(order.id).then((deductionResult) => {
      if (deductionResult.deducted && deductionResult.items.length > 0) {
        console.log(`[STOCK] Order ${order.orderNumber}: deducted ${deductionResult.items.length} ingredients`);

        // Emit stock:updated for each deducted ingredient
        const io2 = getIO();
        if (io2) {
          for (const item of deductionResult.items) {
            if (item.quantity > 0) {
              io2.to(`outlet:${order.outletId}:stock`).emit('stock:updated', {
                outletId: order.outletId,
                ingredientId: item.ingredientId,
                ingredientName: item.ingredientName,
                quantity: item.quantity,
                source: item.source,
                orderId: order.id,
              });

              // Check for anomaly after each deduction
              detectAnomalyAfterDeduction(order.outletId, item.ingredientId, item.quantity).then((alert) => {
                if (alert) {
                  io2.to(`outlet:${order.outletId}:stock`).emit('stock:anomaly', alert);
                  console.log(`[ANOMALY] ${alert.ingredientName}: ${alert.severity} (zScore=${alert.zScore.toFixed(1)})`);
                }
              }).catch(() => {});
            }
          }
        }
      }
      if (deductionResult.skippedIngredients.length > 0) {
        console.log(`[STOCK] Order ${order.orderNumber}: ${deductionResult.skippedIngredients.length} ingredients skipped (cold start)`);
      }
      if (deductionResult.errors.length > 0) {
        console.warn(`[STOCK] Order ${order.orderNumber} errors:`, deductionResult.errors);
      }
    }).catch((err) => {
      console.error(`[STOCK] Failed to deduct for order ${order.orderNumber}:`, err.message);
    });
  } catch (err: any) {
    console.error('[ORDER_CREATE]', err);
    res.status(500).json({ error: 'Failed to create order', message: err.message });
  }
});

// @FLOW:     ORDER_STATUS
// @CALLS:    DB:orders SELECT
// @MUTATES:  none
publicRouter.get('/orders/:id', async (req, res) => {
  try {
    const order = await prisma.order.findUnique({
      where: { id: req.params.id },
      include: {
        items: { include: { modifiers: true } },
        payment: true,
      },
    });

    if (!order) {
      return res.status(404).json({ error: 'Order not found' });
    }

    res.json(order);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch order', message: err.message });
  }
});

// ══════════════════════════════════════════════════
// ── GROUP ORDER ROUTES ──
// ══════════════════════════════════════════════════

// @FLOW:     GROUP_ORDER_CREATE
// @CALLS:    DB:group_orders INSERT, DB:group_order_members INSERT
// @MUTATES:  DB:group_orders (INSERT), DB:group_order_members (INSERT)
publicRouter.post('/group-orders', async (req, res) => {
  try {
    const { outletId, hostName, code } = req.body;

    if (!outletId || !hostName || !code) {
      return res.status(400).json({ error: 'outletId, hostName, and code are required' });
    }

    // Validate outlet exists
    const outlet = await prisma.outlet.findUnique({ where: { id: outletId, isActive: true } });
    if (!outlet) {
      return res.status(404).json({ error: 'Outlet not found' });
    }

    // Check code uniqueness
    const existing = await prisma.groupOrder.findUnique({ where: { code } });
    if (existing) {
      return res.status(409).json({ error: 'Code already in use, please try again' });
    }

    const now = new Date();
    const expiresAt = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2 hours

    const groupOrder = await prisma.groupOrder.create({
      data: {
        code,
        outletId,
        hostId: `host-${Date.now()}`,
        hostName,
        status: 'ACTIVE',
        maxMembers: 10,
        expiresAt,
        members: {
          create: {
            name: hostName,
            totalAmount: 0,
          },
        },
      },
      include: {
        members: true,
      },
    });

    // Format response for frontend store
    const response = formatGroupOrderResponse(groupOrder);
    res.status(201).json(response);
  } catch (err: any) {
    console.error('[GROUP_ORDER_CREATE]', err);
    res.status(500).json({ error: 'Failed to create group order', message: err.message });
  }
});

// @FLOW:     GROUP_ORDER_GET
// @CALLS:    DB:group_orders SELECT
// @MUTATES:  none
publicRouter.get('/group-orders/:code', async (req, res) => {
  try {
    const groupOrder = await prisma.groupOrder.findUnique({
      where: { code: req.params.code.toUpperCase() },
      include: {
        members: {
          include: { items: true },
        },
      },
    });

    if (!groupOrder) {
      return res.status(404).json({ error: 'Group order not found' });
    }

    // Auto-expire if past expiresAt
    if (groupOrder.status === 'ACTIVE' && new Date() > groupOrder.expiresAt) {
      await prisma.groupOrder.update({
        where: { id: groupOrder.id },
        data: { status: 'CANCELLED' },
      });
      groupOrder.status = 'CANCELLED';
    }

    const response = formatGroupOrderResponse(groupOrder);
    res.json(response);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch group order', message: err.message });
  }
});

// @FLOW:     GROUP_ORDER_JOIN
// @CALLS:    DB:group_orders SELECT, DB:group_order_members INSERT
// @MUTATES:  DB:group_order_members (INSERT)
publicRouter.post('/group-orders/:code/join', async (req, res) => {
  try {
    const { memberName } = req.body;
    if (!memberName) {
      return res.status(400).json({ error: 'memberName is required' });
    }

    const groupOrder = await prisma.groupOrder.findUnique({
      where: { code: req.params.code.toUpperCase() },
      include: { members: true },
    });

    if (!groupOrder) {
      return res.status(404).json({ error: 'Group order not found' });
    }

    if (groupOrder.status !== 'ACTIVE') {
      return res.status(400).json({ error: 'Group order is not active' });
    }

    if (new Date() > groupOrder.expiresAt) {
      await prisma.groupOrder.update({
        where: { id: groupOrder.id },
        data: { status: 'CANCELLED' },
      });
      return res.status(400).json({ error: 'Group order has expired' });
    }

    if (groupOrder.members.length >= groupOrder.maxMembers) {
      return res.status(400).json({ error: 'Group is full' });
    }

    // Check duplicate name
    const duplicateName = groupOrder.members.find(
      (m) => m.name.toLowerCase() === memberName.toLowerCase()
    );
    if (duplicateName) {
      return res.status(400).json({ error: 'Name already taken in this group' });
    }

    // Add member
    const updated = await prisma.groupOrder.update({
      where: { id: groupOrder.id },
      data: {
        members: {
          create: {
            name: memberName,
            totalAmount: 0,
          },
        },
      },
      include: {
        members: { include: { items: true } },
      },
    });

    const response = formatGroupOrderResponse(updated);
    res.json(response);
  } catch (err: any) {
    console.error('[GROUP_ORDER_JOIN]', err);
    res.status(500).json({ error: 'Failed to join group order', message: err.message });
  }
});

// @FLOW:     GROUP_ORDER_ADD_ITEM
// @CALLS:    DB:group_orders SELECT, DB:group_order_member_items INSERT
// @MUTATES:  DB:group_order_member_items (INSERT), DB:group_order_members (UPDATE totalAmount)
publicRouter.post('/group-orders/:code/items', async (req, res) => {
  try {
    const { memberId, item } = req.body;

    if (!memberId || !item) {
      return res.status(400).json({ error: 'memberId and item are required' });
    }

    const groupOrder = await prisma.groupOrder.findUnique({
      where: { code: req.params.code.toUpperCase() },
      include: { members: true },
    });

    if (!groupOrder) {
      return res.status(404).json({ error: 'Group order not found' });
    }

    if (groupOrder.status !== 'ACTIVE') {
      return res.status(400).json({ error: 'Group order is not active' });
    }

    // Validate member exists in group
    const member = groupOrder.members.find((m) => m.id === memberId);
    if (!member) {
      return res.status(404).json({ error: 'Member not found in this group' });
    }

    // Calculate item total
    const itemTotal = (item.price + (item.modifiers || []).reduce(
      (s: number, m: any) => s + (m.priceAdjustment || 0), 0
    )) * item.quantity;

    // Add item to member
    await prisma.groupOrderMemberItem.create({
      data: {
        memberId,
        menuId: item.menuId,
        menuName: item.menuName,
        price: item.price,
        quantity: item.quantity,
        modifiers: item.modifiers || [],
        notes: item.notes || null,
      },
    });

    // Update member total
    await prisma.groupOrderMember.update({
      where: { id: memberId },
      data: { totalAmount: member.totalAmount + itemTotal },
    });

    // Update group total
    await prisma.groupOrder.update({
      where: { id: groupOrder.id },
      data: { totalAmount: groupOrder.totalAmount + itemTotal },
    });

    // Return updated group
    const updated = await prisma.groupOrder.findUnique({
      where: { id: groupOrder.id },
      include: {
        members: { include: { items: true } },
      },
    });

    const response = formatGroupOrderResponse(updated!);
    res.json(response);
  } catch (err: any) {
    console.error('[GROUP_ORDER_ADD_ITEM]', err);
    res.status(500).json({ error: 'Failed to add item', message: err.message });
  }
});

// @FLOW:     GROUP_ORDER_LOCK
// @CALLS:    DB:group_orders UPDATE
// @MUTATES:  DB:group_orders (status → LOCKED)
publicRouter.put('/group-orders/:code/lock', async (req, res) => {
  try {
    const groupOrder = await prisma.groupOrder.findUnique({
      where: { code: req.params.code.toUpperCase() },
      include: { members: true },
    });

    if (!groupOrder) {
      return res.status(404).json({ error: 'Group order not found' });
    }

    if (groupOrder.status !== 'ACTIVE') {
      return res.status(400).json({ error: 'Group order is not active' });
    }

    // Check at least one member has items
    const hasItems = groupOrder.members.some((m) => m.totalAmount > 0);
    if (!hasItems) {
      return res.status(400).json({ error: 'No items in group order' });
    }

    const updated = await prisma.groupOrder.update({
      where: { id: groupOrder.id },
      data: { status: 'LOCKED' },
      include: {
        members: { include: { items: true } },
      },
    });

    const response = formatGroupOrderResponse(updated);
    res.json(response);
  } catch (err: any) {
    console.error('[GROUP_ORDER_LOCK]', err);
    res.status(500).json({ error: 'Failed to lock group order', message: err.message });
  }
});

// ── Helper: Format GroupOrder with members for frontend ──
// @FLOW:     FORMAT_RESPONSE
// @CALLS:    none
// @MUTATES:  none
function formatGroupOrderResponse(groupOrder: any) {
  return {
    id: groupOrder.id,
    code: groupOrder.code,
    outletId: groupOrder.outletId,
    hostId: groupOrder.hostId,
    hostName: groupOrder.hostName,
    status: groupOrder.status,
    totalAmount: groupOrder.totalAmount,
    maxMembers: groupOrder.maxMembers,
    createdAt: groupOrder.createdAt.toISOString(),
    expiresAt: groupOrder.expiresAt.toISOString(),
    members: groupOrder.members.map((member: any) => ({
      id: member.id,
      name: member.name,
      avatar: member.avatar || undefined,
      items: (member.items || []).map((item: any) => ({
        menuId: item.menuId,
        menuName: item.menuName,
        price: item.price,
        quantity: item.quantity,
        modifiers: item.modifiers || [],
        notes: item.notes || undefined,
      })),
      totalAmount: member.totalAmount,
      paidAt: member.paidAt ? member.paidAt.toISOString() : undefined,
    })),
  };
}
