// @WHO:   vps-deploy/api/kds/src/routes/stock.ts
// @WHAT:  Stock management routes — ingredient CRUD, stock adjustments, alerts, logs
// @PART:  api
// @ENTRY: stockRouter

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

export const stockRouter = Router();

// All stock routes require auth
stockRouter.use(authMiddleware);

// ── Ingredients ──

// @FLOW:     INGREDIENT_LIST
// @CALLS:    DB:outlet_ingredients SELECT (include ingredient)
// @MUTATES:  none
stockRouter.get('/outlets/:id/ingredients', async (req, res) => {
  try {
    const ingredients = await prisma.outletIngredient.findMany({
      where: { outletId: req.params.id },
      include: { ingredient: true },
      orderBy: { ingredient: { name: 'asc' } },
    });

    res.json(ingredients);
  } 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 INSERT
// @MUTATES:  DB:ingredients (INSERT), DB:outlet_ingredients (INSERT)
stockRouter.post('/outlets/:id/ingredients', async (req, res) => {
  try {
    const { name, unit, costPerUnit, yieldPercent, minimumQuantity, stock, minStock, parLevel, autoDeduct } = req.body;
    if (!name) return res.status(400).json({ error: 'name is required' });

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

    const outletIngredient = await prisma.outletIngredient.create({
      data: {
        outletId: req.params.id,
        ingredientId: ingredient.id,
        stock: stock ?? 0,
        minStock: minStock ?? 0,
        parLevel: parLevel ?? 0,
        autoDeduct: autoDeduct ?? false,
      },
      include: { ingredient: true },
    });

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

// @FLOW:     INGREDIENT_UPDATE
// @CALLS:    DB:ingredients UPDATE, DB:outlet_ingredients UPDATE
// @MUTATES:  DB:ingredients (UPDATE), DB:outlet_ingredients (UPDATE)
stockRouter.put('/outlets/:id/ingredients/:ingredientId', async (req, res) => {
  try {
    const { ingredientId } = req.params;
    const { name, unit, costPerUnit, yieldPercent, minimumQuantity, stock, minStock, parLevel, autoDeduct } = req.body;

    // Update global Ingredient fields
    const ingredientData: any = {};
    if (name !== undefined) ingredientData.name = name;
    if (unit !== undefined) ingredientData.unit = unit;
    if (costPerUnit !== undefined) ingredientData.costPerUnit = costPerUnit;
    if (yieldPercent !== undefined) ingredientData.yieldPercent = yieldPercent;
    if (minimumQuantity !== undefined) ingredientData.minimumQuantity = minimumQuantity;

    if (Object.keys(ingredientData).length > 0) {
      await prisma.ingredient.update({
        where: { id: ingredientId },
        data: ingredientData,
      });
    }

    // Update outlet-specific fields
    const outletData: any = {};
    if (stock !== undefined) outletData.stock = stock;
    if (minStock !== undefined) outletData.minStock = minStock;
    if (parLevel !== undefined) outletData.parLevel = parLevel;
    if (autoDeduct !== undefined) outletData.autoDeduct = autoDeduct;

    let outletIngredient;
    if (Object.keys(outletData).length > 0) {
      outletIngredient = await prisma.outletIngredient.update({
        where: {
          outletId_ingredientId: {
            outletId: req.params.id,
            ingredientId,
          },
        },
        data: outletData,
        include: { ingredient: true },
      });
    } else {
      outletIngredient = await prisma.outletIngredient.findUnique({
        where: {
          outletId_ingredientId: {
            outletId: req.params.id,
            ingredientId,
          },
        },
        include: { ingredient: true },
      });
    }

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

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

// ── Stock Adjustment ──

// @FLOW:     STOCK_ADJUST
// @CALLS:    DB:outlet_ingredients SELECT/UPDATE, DB:stock_logs INSERT
// @MUTATES:  DB:outlet_ingredients (UPDATE stock), DB:stock_logs (INSERT)
// @BEHAVIOR: IN increments stock, OUT/WASTE decrement stock; stock never goes below 0;
//            emits stock:updated and conditionally stock:alert via Socket.IO
stockRouter.post('/outlets/:id/stock/adjust', async (req: any, res) => {
  try {
    const outletId = req.params.id;
    const { ingredientId, type, quantity, note, source, costImpact } = req.body;

    if (!ingredientId || !type || quantity === undefined) {
      return res.status(400).json({ error: 'ingredientId, type, and quantity are required' });
    }

    const validTypes = ['IN', 'OUT', 'WASTE', 'ADJUST', 'RETURN', 'TRANSFER'];
    if (!validTypes.includes(type)) {
      return res.status(400).json({ error: `type must be one of: ${validTypes.join(', ')}` });
    }

    // For ADJUST type, quantity can be positive or negative (it's a delta).
    // For all other types, quantity must be positive (direction is determined by type).
    if (type !== 'ADJUST' && quantity <= 0) {
      return res.status(400).json({ error: 'quantity must be positive for this type' });
    }
    if (type === 'ADJUST' && quantity === 0) {
      return res.status(400).json({ error: 'quantity cannot be zero for ADJUST' });
    }

    // Validate ingredient exists in this outlet
    const oi = await prisma.outletIngredient.findUnique({
      where: { outletId_ingredientId: { outletId, ingredientId } },
      include: { ingredient: true },
    });

    if (!oi) {
      return res.status(404).json({ error: 'Ingredient not found in this outlet' });
    }

    // Calculate new stock based on adjustment type
    let newStock = oi.stock;
    let logQuantity = quantity;

    switch (type) {
      case 'IN':
      case 'RETURN':
        newStock = oi.stock + quantity;
        break;
      case 'OUT':
      case 'WASTE':
      case 'TRANSFER':
        newStock = oi.stock - quantity;
        logQuantity = -quantity;
        break;
      case 'ADJUST':
        // ADJUST: quantity is the delta — positive to add, negative to subtract
        newStock = oi.stock + quantity;
        logQuantity = quantity;
        break;
    }

    // Never let stock go below 0
    if (newStock < 0) {
      return res.status(400).json({
        error: 'Insufficient stock',
        currentStock: oi.stock,
        requested: quantity,
        type,
      });
    }

    // Update stock and create log in a transaction
    const [updatedOi, stockLog] = await prisma.$transaction([
      prisma.outletIngredient.update({
        where: { outletId_ingredientId: { outletId, ingredientId } },
        data: { stock: newStock },
        include: { ingredient: true },
      }),
      prisma.stockLog.create({
        data: {
          outletId,
          ingredientId,
          type,
          quantity: logQuantity,
          note: note || null,
          source: source || 'MANUAL',
          costImpact: costImpact ?? 0,
        },
      }),
    ]);

    // Emit stock:updated via Socket.IO
    const io = getIO();
    if (io) {
      const payload = {
        outletId,
        ingredientId,
        ingredientName: oi.ingredient.name,
        previousStock: oi.stock,
        newStock,
        type,
        quantity,
        stockLogId: stockLog.id,
      };
      io.to(`outlet:${outletId}:stock`).emit('stock:updated', payload);
    }

    // Check for low-stock condition after adjustment
    if (newStock <= oi.minStock) {
      if (io) {
        io.to(`outlet:${outletId}:stock`).emit('stock:alert', {
          outletId,
          ingredientId,
          ingredientName: oi.ingredient.name,
          currentStock: newStock,
          minStock: oi.minStock,
          unit: oi.ingredient.unit,
          type: 'LOW_STOCK',
        });
      }
    }

    res.json({
      outletIngredient: updatedOi,
      stockLog,
      previousStock: oi.stock,
      newStock,
    });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to adjust stock', message: err.message });
  }
});

// ── Stock Alerts ──

// @FLOW:     STOCK_ALERTS
// @CALLS:    DB:outlet_ingredients SELECT (filter: stock <= minStock)
// @MUTATES:  none
stockRouter.get('/outlets/:id/stock/alerts', async (req, res) => {
  try {
    const outletId = req.params.id;

    // Prisma doesn't support cross-field comparison (stock <= minStock) in where,
    // so fetch all for this outlet and filter in application layer
    const allItems = await prisma.outletIngredient.findMany({
      where: { outletId },
      include: { ingredient: true },
      orderBy: { stock: 'asc' },
    });

    const alerts = allItems.filter((item) => item.stock <= item.minStock);

    res.json({
      outletId,
      alertCount: alerts.length,
      alerts: alerts.map((item) => ({
        outletIngredientId: item.id,
        ingredientId: item.ingredientId,
        ingredientName: item.ingredient.name,
        unit: item.ingredient.unit,
        currentStock: item.stock,
        minStock: item.minStock,
        parLevel: item.parLevel,
        shortfall: item.minStock - item.stock,
      })),
    });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch stock alerts', message: err.message });
  }
});

// ── Stock Logs ──

// @FLOW:     STOCK_LOGS
// @CALLS:    DB:stock_logs SELECT (paginated, filtered)
// @MUTATES:  none
stockRouter.get('/outlets/:id/stock/logs', async (req, res) => {
  try {
    const outletId = req.params.id;
    const { ingredientId, type, limit, offset } = req.query;

    const where: any = { outletId };
    if (ingredientId) where.ingredientId = ingredientId as string;
    if (type) where.type = type as string;

    const take = limit ? parseInt(limit as string) : 50;
    const skip = offset ? parseInt(offset as string) : 0;

    const [logs, total] = await Promise.all([
      prisma.stockLog.findMany({
        where,
        include: { ingredient: { select: { id: true, name: true, unit: true } } },
        orderBy: { createdAt: 'desc' },
        take,
        skip,
      }),
      prisma.stockLog.count({ where }),
    ]);

    res.json({ logs, total, limit: take, offset: skip });
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to fetch stock logs', message: err.message });
  }
});
