// @WHO:   vps-deploy/api/kds/src/routes/v1.ts
// @WHAT:  V1 API routes — authenticated endpoints for outlets, KDS, stock
// @PART:  api
// @ENTRY: v1Router

import { Router } from 'express';
import { prisma } from '../lib/prisma';
import { authMiddleware } from '../middleware/auth';
import { getIO } from '../lib/socket';

export const v1Router = Router();

// All v1 routes require auth
v1Router.use(authMiddleware);

// ── Outlets ──

// @FLOW:     OUTLET_LIST
// @CALLS:    DB:outlets SELECT (filtered by user's outlets)
// @MUTATES:  none
v1Router.get('/outlets', async (req: any, res) => {
  try {
    const user = req.user;
    let outlets;

    if (user.role === 'SUPER_ADMIN') {
      outlets = await prisma.outlet.findMany({ where: { isActive: true } });
    } else {
      outlets = await prisma.outlet.findMany({
        where: {
          isActive: true,
          users: { some: { userId: user.userId } },
        },
      });
    }

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

v1Router.get('/outlets/:id', async (req: any, res) => {
  try {
    const outlet = await prisma.outlet.findUnique({
      where: { id: req.params.id },
      include: {
        categories: { orderBy: { sortOrder: 'asc' } },
        stations: true,
        _count: { select: { orders: true, menus: 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 });
  }
});

// ── KDS Tasks ──

// @FLOW:     KDS_TASKS_FETCH
// @CALLS:    DB:kds_tasks SELECT, DB:orders SELECT
// @MUTATES:  none
v1Router.get('/outlets/:id/kds/tasks', async (req, res) => {
  try {
    const { status, stationId } = req.query;
    const where: any = { station: { outletId: req.params.id } };

    if (status) where.status = status;
    if (stationId) where.stationId = stationId;

    const tasks = await prisma.kdsTask.findMany({
      where,
      include: {
        order: {
          include: {
            items: { include: { modifiers: true } },
          },
        },
        station: true,
        logs: { orderBy: { createdAt: 'desc' }, take: 10 },
      },
      orderBy: { createdAt: 'asc' },
    });

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

// @FLOW:     KDS_TASK_UPDATE
// @CALLS:    DB:kds_tasks UPDATE, DB:task_logs INSERT
// @MUTATES:  DB:kds_tasks (UPDATE status/timestamps), DB:task_logs (INSERT)
v1Router.patch('/outlets/:id/kds/tasks/:taskId', async (req: any, res) => {
  try {
    const { status } = req.body;
    const userId = req.user?.userId;

    const existing = await prisma.kdsTask.findUnique({
      where: { id: req.params.taskId },
    });

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

    const updateData: any = { status };

    if (status === 'IN_PROGRESS' && !existing.startedAt) {
      updateData.startedAt = new Date();
    }
    if (status === 'DONE') {
      updateData.completedAt = new Date();
    }

    // Calculate urgency based on time elapsed
    if (status === 'PENDING' || status === 'IN_PROGRESS') {
      const elapsed = Date.now() - existing.createdAt.getTime();
      const minutesElapsed = elapsed / 60000;
      if (minutesElapsed > 20) {
        updateData.urgency = 'URGENT';
      } else if (minutesElapsed > 10) {
        updateData.urgency = 'WARNING';
      }
    }

    const task = await prisma.kdsTask.update({
      where: { id: req.params.taskId },
      data: updateData,
    });

    // Log the action
    await prisma.taskLog.create({
      data: {
        taskId: task.id,
        action: status === 'IN_PROGRESS' ? 'STARTED' : status === 'DONE' ? 'COMPLETED' : 'BUMPED',
        userId,
      },
    });

    // If all tasks for this order are done, update order status
    if (status === 'DONE') {
      const pendingTasks = await prisma.kdsTask.count({
        where: {
          orderId: task.orderId,
          status: { not: 'DONE' },
        },
      });

      if (pendingTasks === 0) {
        await prisma.order.update({
          where: { id: task.orderId },
          data: { status: 'READY' },
        });
      }
    }

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

// ── Stock ──

// @FLOW:     STOCK_FETCH
// @CALLS:    DB:outlet_ingredients SELECT, DB:ingredients SELECT
// @MUTATES:  none
// @BEHAVIOR: Legacy stock summary — prefer /outlets/:id/ingredients from stockRouter for full data
v1Router.get('/outlets/:id/stock', async (req, res) => {
  try {
    const ingredients = await prisma.outletIngredient.findMany({
      where: { outletId: req.params.id },
      include: { ingredient: true },
      orderBy: { ingredient: { name: 'asc' } },
    });

    const lowStock = ingredients.filter(
      (i) => i.stock <= i.minStock
    );

    res.json({
      total: ingredients.length,
      lowStock: lowStock.length,
      items: ingredients.map((i) => ({
        id: i.id,
        name: i.ingredient.name,
        quantity: i.stock,
        unit: i.ingredient.unit,
        minimum: i.minStock,
        isLow: i.stock <= i.minStock,
        autoDeduct: i.autoDeduct,
        parLevel: i.parLevel,
      })),
    });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch stock', message: err.message });
  }
});

// ── Categories ──

// @FLOW:     CATEGORY_LIST
// @CALLS:    DB:categories SELECT
// @MUTATES:  none
v1Router.get('/outlets/:id/categories', async (req, res) => {
  try {
    const categories = await prisma.category.findMany({
      where: { outletId: req.params.id },
      orderBy: { sortOrder: 'asc' },
      include: { _count: { select: { menus: true } } },
    });
    res.json(categories);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch categories', message: err.message });
  }
});

// @FLOW:     CATEGORY_CREATE
// @CALLS:    DB:categories INSERT
// @MUTATES:  DB:categories (INSERT)
v1Router.post('/outlets/:id/categories', async (req: any, res) => {
  try {
    const { name, icon, sortOrder, isActive } = req.body;
    if (!name) return res.status(400).json({ error: 'name is required' });

    const category = await prisma.category.create({
      data: {
        outletId: req.params.id,
        name,
        icon: icon || null,
        sortOrder: sortOrder ?? 0,
        isActive: isActive !== false,
      },
    });
    res.status(201).json(category);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to create category', message: err.message });
  }
});

// @FLOW:     CATEGORY_UPDATE
// @CALLS:    DB:categories UPDATE
// @MUTATES:  DB:categories (UPDATE)
v1Router.put('/outlets/:id/categories/:categoryId', async (req, res) => {
  try {
    const { name, icon, sortOrder, isActive } = req.body;
    const category = await prisma.category.update({
      where: { id: req.params.categoryId },
      data: {
        ...(name !== undefined && { name }),
        ...(icon !== undefined && { icon }),
        ...(sortOrder !== undefined && { sortOrder }),
        ...(isActive !== undefined && { isActive }),
      },
    });
    res.json(category);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to update category', message: err.message });
  }
});

// @FLOW:     CATEGORY_DELETE
// @CALLS:    DB:categories DELETE
// @MUTATES:  DB:categories (DELETE)
v1Router.delete('/outlets/:id/categories/:categoryId', async (req, res) => {
  try {
    await prisma.category.delete({ where: { id: req.params.categoryId } });
    res.json({ deleted: true });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to delete category', message: err.message });
  }
});

// ── Menus ──

// @FLOW:     MENU_LIST
// @CALLS:    DB:menus SELECT
// @MUTATES:  none
v1Router.get('/outlets/:id/menus', async (req, res) => {
  try {
    const { categoryId, isAvailable } = req.query;
    const where: any = { outletId: req.params.id };
    if (categoryId) where.categoryId = categoryId;
    if (isAvailable !== undefined) where.isAvailable = isAvailable === 'true';

    const menus = await prisma.menu.findMany({
      where,
      orderBy: { sortOrder: 'asc' },
      include: {
        category: { select: { id: true, name: true } },
        modifiers: { include: { options: { orderBy: { priceAdjustment: 'asc' } } }, orderBy: { name: 'asc' } },
      },
    });
    res.json(menus);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch menus', message: err.message });
  }
});

// @FLOW:     MENU_CREATE
// @CALLS:    DB:menus INSERT
// @MUTATES:  DB:menus (INSERT)
v1Router.post('/outlets/:id/menus', async (req: any, res) => {
  try {
    const { name, description, price, image, categoryId, isAvailable, sortOrder, modifiers } = req.body;
    if (!name || !categoryId || price === undefined) {
      return res.status(400).json({ error: 'name, categoryId, and price are required' });
    }

    const menu = await prisma.menu.create({
      data: {
        outletId: req.params.id,
        name,
        description: description || null,
        price,
        image: image || null,
        categoryId,
        isAvailable: isAvailable !== false,
        sortOrder: sortOrder ?? 0,
        modifiers: modifiers ? {
          create: modifiers.map((mod: any) => ({
            name: mod.name,
            required: mod.required ?? false,
            maxSelect: mod.maxSelect ?? 1,
            options: mod.options ? {
              create: mod.options.map((opt: any) => ({
                name: opt.name,
                priceAdjustment: opt.priceAdjustment ?? 0,
                isDefault: opt.isDefault ?? false,
              })),
            } : undefined,
          })),
        } : undefined,
      },
      include: { modifiers: { include: { options: true } } },
    });
    res.status(201).json(menu);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to create menu', message: err.message });
  }
});

// @FLOW:     MENU_UPDATE
// @CALLS:    DB:menus UPDATE
// @MUTATES:  DB:menus (UPDATE)
v1Router.put('/outlets/:id/menus/:menuId', async (req, res) => {
  try {
    const { name, description, price, image, categoryId, isAvailable, sortOrder } = req.body;
    const menu = await prisma.menu.update({
      where: { id: req.params.menuId },
      data: {
        ...(name !== undefined && { name }),
        ...(description !== undefined && { description }),
        ...(price !== undefined && { price }),
        ...(image !== undefined && { image }),
        ...(categoryId !== undefined && { categoryId }),
        ...(isAvailable !== undefined && { isAvailable }),
        ...(sortOrder !== undefined && { sortOrder }),
      },
    });
    res.json(menu);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to update menu', message: err.message });
  }
});

// @FLOW:     MENU_DELETE
// @CALLS:    DB:menus DELETE
// @MUTATES:  DB:menus (DELETE)
v1Router.delete('/outlets/:id/menus/:menuId', async (req, res) => {
  try {
    await prisma.menu.delete({ where: { id: req.params.menuId } });
    res.json({ deleted: true });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to delete menu', message: err.message });
  }
});

// ── Ingredients ──

// @FLOW:     INGREDIENT_LIST
// @CALLS:    DB:ingredients SELECT, DB:outlet_ingredients SELECT
// @MUTATES:  none
v1Router.get('/outlets/:id/ingredients', async (req, res) => {
  try {
    // Get all ingredients with their stock info for this outlet
    const outletIngredients = await prisma.outletIngredient.findMany({
      where: { outletId: req.params.id },
      include: { ingredient: true },
      orderBy: { ingredient: { name: 'asc' } },
    });

    // Also get ingredients not yet linked to this outlet
    const linkedIds = outletIngredients.map((oi) => oi.ingredientId);
    const unlinked = await prisma.ingredient.findMany({
      where: { id: { notIn: linkedIds } },
      orderBy: { name: 'asc' },
    });

    res.json({
      linked: outletIngredients.map((oi) => ({
        id: oi.ingredient.id,
        name: oi.ingredient.name,
        unit: oi.ingredient.unit,
        minimumQuantity: oi.ingredient.minimumQuantity,
        quantity: oi.quantity,
        outletIngredientId: oi.id,
      })),
      unlinked: unlinked.map((i) => ({
        id: i.id,
        name: i.name,
        unit: i.unit,
        minimumQuantity: i.minimumQuantity,
        quantity: 0,
        outletIngredientId: null,
      })),
    });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch ingredients', message: err.message });
  }
});

// @FLOW:     INGREDIENT_CREATE
// @CALLS:    DB:ingredients INSERT, DB:outlet_ingredients UPSERT
// @MUTATES:  DB:ingredients (INSERT), DB:outlet_ingredients (UPSERT)
v1Router.post('/outlets/:id/ingredients', async (req, res) => {
  try {
    const { name, unit, minimumQuantity, initialQuantity } = req.body;
    if (!name) return res.status(400).json({ error: 'name is required' });

    const ingredient = await prisma.ingredient.create({
      data: {
        name,
        unit: unit || 'pcs',
        minimumQuantity: minimumQuantity || 0,
      },
    });

    // Link to outlet with initial quantity
    const outletIngredient = await prisma.outletIngredient.upsert({
      where: {
        outletId_ingredientId: {
          outletId: req.params.id,
          ingredientId: ingredient.id,
        },
      },
      create: {
        outletId: req.params.id,
        ingredientId: ingredient.id,
        quantity: initialQuantity || 0,
      },
      update: {}, // No update if already linked
    });

    // Log initial stock if quantity > 0
    if (initialQuantity && initialQuantity > 0) {
      await prisma.stockLog.create({
        data: {
          outletId: req.params.id,
          ingredientId: ingredient.id,
          change: initialQuantity,
          reason: 'INITIAL_STOCK',
        },
      });
    }

    res.status(201).json({ ingredient, outletIngredient });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to create ingredient', message: err.message });
  }
});

// @FLOW:     INGREDIENT_UPDATE
// @CALLS:    DB:ingredients UPDATE
// @MUTATES:  DB:ingredients (UPDATE)
v1Router.put('/outlets/:id/ingredients/:ingredientId', async (req, res) => {
  try {
    const { name, unit, minimumQuantity } = req.body;
    const ingredient = await prisma.ingredient.update({
      where: { id: req.params.ingredientId },
      data: {
        ...(name !== undefined && { name }),
        ...(unit !== undefined && { unit }),
        ...(minimumQuantity !== undefined && { minimumQuantity }),
      },
    });
    res.json(ingredient);
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to update ingredient', message: err.message });
  }
});

// @FLOW:     STOCK_ADJUST
// @CALLS:    DB:outlet_ingredients UPDATE, DB:stock_logs INSERT
// @MUTATES:  DB:outlet_ingredients (UPDATE quantity), DB:stock_logs (INSERT)
// @BEHAVIOR: Adjusts stock by delta (positive = stock in, negative = stock out).
//            Creates OutletIngredient if not yet linked to outlet.
v1Router.patch('/outlets/:id/stock/:ingredientId', async (req, res) => {
  try {
    const { delta, reason } = req.body;
    if (delta === undefined || delta === 0) {
      return res.status(400).json({ error: 'delta is required and cannot be 0' });
    }

    const outletIngredient = await prisma.outletIngredient.upsert({
      where: {
        outletId_ingredientId: {
          outletId: req.params.id,
          ingredientId: req.params.ingredientId,
        },
      },
      create: {
        outletId: req.params.id,
        ingredientId: req.params.ingredientId,
        quantity: delta,
      },
      update: { quantity: { increment: delta } },
    });

    await prisma.stockLog.create({
      data: {
        outletId: req.params.id,
        ingredientId: req.params.ingredientId,
        change: delta,
        reason: reason || (delta > 0 ? 'MANUAL_STOCK_IN' : 'MANUAL_STOCK_OUT'),
      },
    });

    res.json(outletIngredient);

    // Emit socket event
    const io = getIO();
    if (io) {
      io.to(`outlet:${req.params.id}:stock`).emit('stock:updated', { outletId: req.params.id });
    }
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to adjust stock', message: err.message });
  }
});

// ── Employees (Users in Outlet) ──

// @FLOW:     EMPLOYEE_LIST
// @CALLS:    DB:users SELECT via user_outlets
// @MUTATES:  none
v1Router.get('/outlets/:id/employees', async (req, res) => {
  try {
    const userOutlets = await prisma.userOutlet.findMany({
      where: { outletId: req.params.id },
      include: { user: { select: { id: true, name: true, email: true, phone: true, role: true } } },
      orderBy: { user: { name: 'asc' } },
    });

    res.json(userOutlets.map((uo) => ({
      id: uo.user.id,
      name: uo.user.name,
      email: uo.user.email,
      phone: uo.user.phone,
      role: uo.role,
    })));
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch employees', message: err.message });
  }
});

// ── Order Management ──

// @FLOW:     ORDER_LIST
// @CALLS:    DB:orders SELECT
// @MUTATES:  none
v1Router.get('/outlets/:id/orders', async (req, res) => {
  try {
    const { status, paymentMethod, limit, offset } = req.query;
    const where: any = { outletId: req.params.id };
    if (status) where.status = status;
    if (paymentMethod) where.paymentMethod = paymentMethod;

    const orders = await prisma.order.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      take: limit ? parseInt(limit as string) : 50,
      skip: offset ? parseInt(offset as string) : 0,
      include: {
        items: { include: { modifiers: true } },
        payment: true,
      },
    });

    const total = await prisma.order.count({ where });

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

// @FLOW:     ORDER_UPDATE
// @CALLS:    DB:orders UPDATE
// @MUTATES:  DB:orders (UPDATE status)
v1Router.patch('/outlets/:id/orders/:orderId', async (req: any, res) => {
  try {
    const { status } = req.body;
    const userId = req.user?.userId;

    const order = await prisma.order.update({
      where: { id: req.params.orderId },
      data: { status },
    });

    // Audit log
    await prisma.auditLog.create({
      data: {
        userId,
        action: `ORDER_STATUS_${status}`,
        entity: 'Order',
        entityId: order.id,
        details: `Order ${order.orderNumber} status changed to ${status}`,
      },
    });

    res.json(order);

    // Emit Socket.IO event for order update
    const io = getIO();
    if (io) {
      const outletId = req.params.id;
      io.to(`outlet:${outletId}:orders`).emit('order:updated', order);
      io.to(`outlet:${outletId}:kds`).emit('order:updated', order);
      io.to(`outlet:${outletId}:cashier`).emit('order:updated', order);
      console.log(`[SOCKET] Emitted order:updated for order ${order.orderNumber}`);
    }
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to update order', message: err.message });
  }
});

// ── Payments ──

// @FLOW:     PAYMENT_CONFIRM
// @CALLS:    DB:payments UPDATE, DB:orders UPDATE
// @MUTATES:  DB:payments (UPDATE status), DB:orders (UPDATE status)
v1Router.patch('/outlets/:id/payments/:paymentId/confirm', async (req: any, res) => {
  try {
    const payment = await prisma.payment.findUnique({
      where: { id: req.params.paymentId },
      include: { order: true },
    });

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

    const updated = await prisma.payment.update({
      where: { id: req.params.paymentId },
      data: { status: 'paid', paidAt: new Date() },
    });

    // Update order status to RECEIVED (confirmed)
    await prisma.order.update({
      where: { id: payment.orderId },
      data: { status: 'RECEIVED' },
    });

    res.json(updated);

    // Emit Socket.IO event for payment received
    const io = getIO();
    if (io) {
      const outletId = req.params.id;
      io.to(`outlet:${outletId}:orders`).emit('payment:received', {
        orderId: payment.orderId,
        orderNumber: payment.order.orderNumber,
        paymentId: payment.id,
        method: payment.method,
        status: 'paid',
        amount: payment.amount,
      });
      io.to(`outlet:${outletId}:cashier`).emit('payment:received', {
        orderId: payment.orderId,
        orderNumber: payment.order.orderNumber,
        paymentId: payment.id,
        method: payment.method,
        status: 'paid',
        amount: payment.amount,
      });
      console.log(`[SOCKET] Emitted payment:received for order ${payment.order.orderNumber}`);
    }
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to confirm payment', message: err.message });
  }
});
