// @WHO:   vps-deploy/api/kds/src/routes/transformation-task.ts
// @WHAT:  Stock transformation task routes — create, list, complete, cancel prep tasks
// @PART:  task
// @ENTRY: transformationTaskRouter

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

export const transformationTaskRouter = Router();

// All routes require auth
transformationTaskRouter.use(authMiddleware);

// ── Create Task ──

// @FLOW:     TASK_CREATE
// @CALLS:    DB:transformation_tasks INSERT, DB:task_input_items INSERT, DB:task_output_items INSERT
// @MUTATES:  DB:transformation_tasks (INSERT), DB:task_input_items (INSERT), DB:task_output_items (INSERT)
// @BEHAVIOR: Auto-calculates output unitPrice when priceSource is AUTO (cost preservation).
//            Requires at least 1 input item and 1 output item.
//            Only OUTLET_ADMIN and SUPER_ADMIN can create tasks.
transformationTaskRouter.post('/outlets/:id/tasks', async (req: any, res) => {
  try {
    const { title, description, assignedToId, inputItems, outputItems } = req.body;
    const outletId = req.params.id;
    const createdById = req.user.userId;

    // Validation
    if (!title) return res.status(400).json({ error: 'title is required' });
    if (!assignedToId) return res.status(400).json({ error: 'assignedToId is required' });
    if (!inputItems?.length) return res.status(400).json({ error: 'At least 1 input item is required' });
    if (!outputItems?.length) return res.status(400).json({ error: 'At least 1 output item is required' });

    // Verify assigned user exists and belongs to this outlet
    const assignedUser = await prisma.user.findUnique({ where: { id: assignedToId } });
    if (!assignedUser || !assignedUser.isActive) {
      return res.status(400).json({ error: 'Assigned user not found or inactive' });
    }

    // Calculate totals for cost preservation
    let totalInputCost = 0;
    for (const item of inputItems) {
      if (!item.ingredientId || !item.quantity || item.quantity <= 0) {
        return res.status(400).json({ error: 'Each input item needs ingredientId and positive quantity' });
      }
      item.totalPrice = item.quantity * (item.unitPrice || 0);
      totalInputCost += item.totalPrice;
    }

    const totalOutputQty = outputItems.reduce((sum: number, item: any) => {
      if (!item.ingredientId || !item.quantity || item.quantity <= 0) {
        return sum;
      }
      return sum + item.quantity;
    }, 0);

    if (totalOutputQty <= 0) {
      return res.status(400).json({ error: 'Total output quantity must be positive' });
    }

    // Auto-calculate output prices (cost preservation)
    for (const item of outputItems) {
      if (!item.ingredientId || !item.quantity || item.quantity <= 0) {
        return res.status(400).json({ error: 'Each output item needs ingredientId and positive quantity' });
      }
      const priceSource = item.priceSource || 'AUTO';
      if (priceSource === 'AUTO') {
        item.unitPrice = totalInputCost / totalOutputQty;
      }
      item.totalPrice = item.quantity * item.unitPrice;
      item.priceSource = priceSource;
    }

    // Create task with items in a transaction
    const task = await prisma.transformationTask.create({
      data: {
        outletId,
        title,
        description: description || null,
        assignedToId,
        createdById,
        inputItems: {
          create: inputItems.map((item: any) => ({
            ingredientId: item.ingredientId,
            quantity: item.quantity,
            unitPrice: item.unitPrice || 0,
            totalPrice: item.totalPrice,
          })),
        },
        outputItems: {
          create: outputItems.map((item: any) => ({
            ingredientId: item.ingredientId,
            quantity: item.quantity,
            unitPrice: item.unitPrice,
            totalPrice: item.totalPrice,
            priceSource: item.priceSource || 'AUTO',
          })),
        },
      },
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

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

    // Emit socket event to assigned employee
    const io = getIO();
    if (io) {
      io.to(`outlet:${outletId}:tasks`).emit('task:assigned', task);
      io.to(`user:${assignedToId}`).emit('task:assigned', task);
      console.log(`[SOCKET] Emitted task:assigned for task ${task.id}`);
    }
  } catch (err: any) {
    console.error('[TASK_CREATE]', err);
    res.status(500).json({ error: 'Failed to create task', message: err.message });
  }
});

// ── List Tasks ──

// @FLOW:     TASK_LIST
// @CALLS:    DB:transformation_tasks SELECT
// @MUTATES:  none
transformationTaskRouter.get('/outlets/:id/tasks', async (req: any, res) => {
  try {
    const { status, assignedToId, limit, offset } = req.query;
    const where: any = { outletId: req.params.id };

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

    const tasks = await prisma.transformationTask.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      take: limit ? parseInt(limit as string) : 50,
      skip: offset ? parseInt(offset as string) : 0,
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

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

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

// ── Task Detail ──

// @FLOW:     TASK_DETAIL
// @CALLS:    DB:transformation_tasks SELECT
// @MUTATES:  none
transformationTaskRouter.get('/outlets/:id/tasks/:taskId', async (req, res) => {
  try {
    const task = await prisma.transformationTask.findUnique({
      where: { id: req.params.taskId },
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

    if (!task || task.outletId !== req.params.id) {
      return res.status(404).json({ error: 'Task not found' });
    }

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

// ── My Tasks (Employee) ──

// @FLOW:     TASK_MY_LIST
// @CALLS:    DB:transformation_tasks SELECT
// @MUTATES:  none
// @BEHAVIOR: Returns only tasks assigned to the logged-in user, filtered by active status.
transformationTaskRouter.get('/outlets/:id/tasks/my', async (req: any, res) => {
  try {
    const userId = req.user.userId;
    const { status } = req.query;
    const where: any = {
      outletId: req.params.id,
      assignedToId: userId,
    };

    if (status) where.status = status;
    else where.status = { in: ['PENDING', 'IN_PROGRESS'] };

    const tasks = await prisma.transformationTask.findMany({
      where,
      orderBy: { createdAt: 'asc' },
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

    // Also get completed count for stats
    const completedCount = await prisma.transformationTask.count({
      where: {
        outletId: req.params.id,
        assignedToId: userId,
        status: 'COMPLETED',
      },
    });

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

// ── Start Task ──

// @FLOW:     TASK_START
// @CALLS:    DB:transformation_tasks UPDATE
// @MUTATES:  DB:transformation_tasks (UPDATE status → IN_PROGRESS)
transformationTaskRouter.patch('/outlets/:id/tasks/:taskId/start', async (req: any, res) => {
  try {
    const userId = req.user.userId;
    const task = await prisma.transformationTask.findUnique({
      where: { id: req.params.taskId },
    });

    if (!task || task.outletId !== req.params.id) {
      return res.status(404).json({ error: 'Task not found' });
    }

    if (task.status !== 'PENDING') {
      return res.status(400).json({ error: 'Only PENDING tasks can be started' });
    }

    // Only assigned user or admin can start
    if (task.assignedToId !== userId && req.user.role !== 'OUTLET_ADMIN' && req.user.role !== 'SUPER_ADMIN') {
      return res.status(403).json({ error: 'Not authorized to start this task' });
    }

    const updated = await prisma.transformationTask.update({
      where: { id: req.params.taskId },
      data: { status: 'IN_PROGRESS' },
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

    res.json(updated);

    const io = getIO();
    if (io) {
      io.to(`outlet:${task.outletId}:tasks`).emit('task:updated', updated);
      console.log(`[SOCKET] Emitted task:updated for task ${updated.id}`);
    }
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to start task', message: err.message });
  }
});

// ── Complete Task ──

// @FLOW:     TASK_COMPLETE
// @CALLS:    DB:transformation_tasks UPDATE, DB:outlet_ingredients UPDATE/UPSERT, DB:stock_logs INSERT
// @MUTATES:  DB:transformation_tasks (UPDATE status → COMPLETED), DB:outlet_ingredients (UPDATE quantity),
//            DB:stock_logs (INSERT for input deductions + output additions)
// @BEHAVIOR: Runs all stock mutations in a single Prisma transaction. If any mutation fails,
//            the entire task completion is rolled back. Stock deduction happens atomically.
transformationTaskRouter.patch('/outlets/:id/tasks/:taskId/complete', async (req: any, res) => {
  try {
    const userId = req.user.userId;
    const task = await prisma.transformationTask.findUnique({
      where: { id: req.params.taskId },
      include: {
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

    if (!task || task.outletId !== req.params.id) {
      return res.status(404).json({ error: 'Task not found' });
    }

    if (task.status !== 'PENDING' && task.status !== 'IN_PROGRESS') {
      return res.status(400).json({ error: 'Only PENDING or IN_PROGRESS tasks can be completed' });
    }

    // Only assigned user or admin can complete
    if (task.assignedToId !== userId && req.user.role !== 'OUTLET_ADMIN' && req.user.role !== 'SUPER_ADMIN') {
      return res.status(403).json({ error: 'Not authorized to complete this task' });
    }

    // Verify sufficient stock for all input items
    for (const inputItem of task.inputItems) {
      const outletIngredient = await prisma.outletIngredient.findUnique({
        where: {
          outletId_ingredientId: {
            outletId: task.outletId,
            ingredientId: inputItem.ingredientId,
          },
        },
      });

      if (!outletIngredient || outletIngredient.quantity < inputItem.quantity) {
        return res.status(400).json({
          error: `Insufficient stock for ${inputItem.ingredient.name}. Available: ${outletIngredient?.quantity || 0}, Required: ${inputItem.quantity}`,
        });
      }
    }

    // Execute stock transformation in transaction
    const completedTask = await prisma.$transaction(async (tx) => {
      // 1. Deduct all input items from stock
      for (const inputItem of task.inputItems) {
        await tx.outletIngredient.update({
          where: {
            outletId_ingredientId: {
              outletId: task.outletId,
              ingredientId: inputItem.ingredientId,
            },
          },
          data: { quantity: { decrement: inputItem.quantity } },
        });

        // Log stock deduction
        await tx.stockLog.create({
          data: {
            outletId: task.outletId,
            ingredientId: inputItem.ingredientId,
            change: -inputItem.quantity,
            reason: `TRANSFORMATION_INPUT: ${task.title}`,
          },
        });
      }

      // 2. Add all output items to stock
      for (const outputItem of task.outputItems) {
        // Upsert: create if doesn't exist, increment if it does
        await tx.outletIngredient.upsert({
          where: {
            outletId_ingredientId: {
              outletId: task.outletId,
              ingredientId: outputItem.ingredientId,
            },
          },
          create: {
            outletId: task.outletId,
            ingredientId: outputItem.ingredientId,
            quantity: outputItem.quantity,
          },
          update: { quantity: { increment: outputItem.quantity } },
        });

        // Log stock addition
        await tx.stockLog.create({
          data: {
            outletId: task.outletId,
            ingredientId: outputItem.ingredientId,
            change: outputItem.quantity,
            reason: `TRANSFORMATION_OUTPUT: ${task.title}`,
          },
        });
      }

      // 3. Update task status
      return tx.transformationTask.update({
        where: { id: task.id },
        data: {
          status: 'COMPLETED',
          completedAt: new Date(),
        },
        include: {
          assignedTo: { select: { id: true, name: true } },
          createdBy: { select: { id: true, name: true } },
          inputItems: { include: { ingredient: true } },
          outputItems: { include: { ingredient: true } },
        },
      });
    });

    res.json(completedTask);

    // Emit socket events
    const io = getIO();
    if (io) {
      io.to(`outlet:${task.outletId}:tasks`).emit('task:completed', completedTask);
      io.to(`outlet:${task.outletId}:stock`).emit('stock:updated', { outletId: task.outletId });
      io.to(`user:${task.assignedToId}`).emit('task:completed', completedTask);
      console.log(`[SOCKET] Emitted task:completed for task ${completedTask.id}`);
    }

    // Check for low-stock alerts on affected ingredients
    const affectedIngredientIds = [
      ...task.inputItems.map((i) => i.ingredientId),
      ...task.outputItems.map((i) => i.ingredientId),
    ];

    for (const ingredientId of affectedIngredientIds) {
      const outletIngredient = await prisma.outletIngredient.findUnique({
        where: {
          outletId_ingredientId: { outletId: task.outletId, ingredientId },
        },
        include: { ingredient: true },
      });

      if (outletIngredient && outletIngredient.quantity <= outletIngredient.ingredient.minimumQuantity) {
        if (io) {
          io.to(`outlet:${task.outletId}:stock`).emit('stock:alert', {
            outletId: task.outletId,
            ingredientName: outletIngredient.ingredient.name,
            currentQuantity: outletIngredient.quantity,
            minimumQuantity: outletIngredient.ingredient.minimumQuantity,
            unit: outletIngredient.ingredient.unit,
          });
        }
      }
    }
  } catch (err: any) {
    console.error('[TASK_COMPLETE]', err);
    res.status(500).json({ error: 'Failed to complete task', message: err.message });
  }
});

// ── Cancel Task ──

// @FLOW:     TASK_CANCEL
// @CALLS:    DB:transformation_tasks UPDATE
// @MUTATES:  DB:transformation_tasks (UPDATE status → CANCELLED)
// @BEHAVIOR: Only PENDING or IN_PROGRESS tasks can be cancelled. No stock mutation happens on cancel.
transformationTaskRouter.patch('/outlets/:id/tasks/:taskId/cancel', async (req: any, res) => {
  try {
    const task = await prisma.transformationTask.findUnique({
      where: { id: req.params.taskId },
    });

    if (!task || task.outletId !== req.params.id) {
      return res.status(404).json({ error: 'Task not found' });
    }

    if (task.status !== 'PENDING' && task.status !== 'IN_PROGRESS') {
      return res.status(400).json({ error: 'Only PENDING or IN_PROGRESS tasks can be cancelled' });
    }

    // Only admin or creator can cancel
    if (req.user.role !== 'OUTLET_ADMIN' && req.user.role !== 'SUPER_ADMIN' && task.createdById !== req.user.userId) {
      return res.status(403).json({ error: 'Not authorized to cancel this task' });
    }

    const cancelled = await prisma.transformationTask.update({
      where: { id: req.params.taskId },
      data: {
        status: 'CANCELLED',
        cancelledAt: new Date(),
      },
      include: {
        assignedTo: { select: { id: true, name: true } },
        createdBy: { select: { id: true, name: true } },
        inputItems: { include: { ingredient: true } },
        outputItems: { include: { ingredient: true } },
      },
    });

    res.json(cancelled);

    // Emit socket events
    const io = getIO();
    if (io) {
      io.to(`outlet:${task.outletId}:tasks`).emit('task:cancelled', cancelled);
      io.to(`user:${task.assignedToId}`).emit('task:cancelled', cancelled);
      console.log(`[SOCKET] Emitted task:cancelled for task ${cancelled.id}`);
    }
  } catch (err: any) {
    res.status(500).json({ error: 'Failed to cancel task', message: err.message });
  }
});
