const { Router } = require("express");
const prisma = require("../prisma");
const { requireAuth } = require("../middleware/auth");
const { requireInventoryRead, requireInventoryWrite } = require("../middleware/perms");
const { adjustStock, stockTransition } = require("../lib/inventory");
const { requireActiveBranch } = require("../middleware/branch");

const router = Router();

function normalizeOptionalString(input) {
  if (input === undefined) return undefined;
  if (input === null || input === "") return null;
  const s = String(input).trim();
  return s || null;
}

function prismaConflict(err, res) {
  if (err.code === "P2002") {
    return res.status(409).json({ error: "Reference already exists" });
  }
  return null;
}

function parseLines(lines) {
  if (!Array.isArray(lines)) return [];
  return lines
    .map((l) => ({
      productId: l?.productId ? String(l.productId).trim() : "",
      quantity: Number.isFinite(Number(l?.quantity)) ? Math.floor(Number(l.quantity)) : 0,
    }))
    .filter((l) => l.productId && l.quantity !== 0);
}

function sumAbsQty(lines) {
  return (lines || []).reduce((acc, l) => acc + Math.abs(Number(l.quantity) || 0), 0);
}

router.get("/", requireAuth, requireActiveBranch, requireInventoryRead, async (req, res, next) => {
  try {
    const page = Math.max(1, parseInt(req.query.page) || 1);
    const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 25));
    const skip = (page - 1) * limit;
    const search = (req.query.search || "").trim();

    const where = { branchId: req.activeBranchId };
    if (search) {
      where.OR = [
        { reference: { contains: search, mode: "insensitive" } },
        { reason: { contains: search, mode: "insensitive" } },
      ];
    }

    const [total, rows] = await Promise.all([
      prisma.inventoryAdjustment.count({ where }),
      prisma.inventoryAdjustment.findMany({
        where,
        orderBy: [{ updatedAt: "desc" }],
        skip,
        take: limit,
        include: {
          warehouse: { select: { id: true, name: true } },
          lines: { select: { quantity: true } },
        },
      }),
    ]);

    const data = rows.map((r) => ({
      ...r,
      warehouseName: r.warehouse?.name || null,
      lineCount: r.lines?.length || 0,
      totalQuantity: sumAbsQty(r.lines),
      lines: undefined,
      warehouse: undefined,
    }));

    return res.json({
      data,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    });
  } catch (err) {
    return next(err);
  }
});

router.get("/:id", requireAuth, requireActiveBranch, requireInventoryRead, async (req, res, next) => {
  try {
    const row = await prisma.inventoryAdjustment.findFirst({
      where: { id: req.params.id, branchId: req.activeBranchId },
      include: {
        warehouse: { select: { id: true, name: true } },
        lines: {
          orderBy: { id: "asc" },
          include: { product: { select: { id: true, name: true, sku: true } } },
        },
      },
    });
    if (!row) return res.status(404).json({ error: "Not found" });
    return res.json(row);
  } catch (err) {
    return next(err);
  }
});

router.post("/", requireAuth, requireActiveBranch, requireInventoryWrite, async (req, res, next) => {
  try {
    const body = req.body || {};
    const warehouseId = String(body.warehouseId || "").trim();
    if (!warehouseId) return res.status(400).json({ error: "Missing warehouseId" });

    const type = String(body.type || "Increase");
    if (!["Increase", "Decrease"].includes(type)) {
      return res.status(400).json({ error: "Invalid type" });
    }

    const status = String(body.status || "Draft");
    if (!["Draft", "Posted"].includes(status)) {
      return res.status(400).json({ error: "Invalid status" });
    }

    const lines = parseLines(body.lines);
    const row = await prisma.$transaction(async (tx) => {
      const created = await tx.inventoryAdjustment.create({
        data: {
          reference: normalizeOptionalString(body.reference),
          warehouseId,
          branchId: req.activeBranchId,
          type,
          status,
          reason: normalizeOptionalString(body.reason),
          notes: normalizeOptionalString(body.notes),
          lines: {
            create: lines.map((l) => ({
              productId: l.productId,
              quantity: l.quantity,
            })),
          },
        },
        include: {
          warehouse: { select: { id: true, name: true } },
          lines: { include: { product: { select: { id: true, name: true, sku: true } } } },
        },
      });

      if (status === "Posted" && lines.length > 0) {
        const direction = type === "Increase" ? 1 : -1;
        await adjustStock(tx, lines, direction, warehouseId);
      }
      return created;
    });
    return res.status(201).json(row);
  } catch (err) {
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.patch("/:id", requireAuth, requireActiveBranch, requireInventoryWrite, async (req, res, next) => {
  try {
    const { id } = req.params;
    const existing = await prisma.inventoryAdjustment.findFirst({ where: { id, branchId: req.activeBranchId } });
    if (!existing) return res.status(404).json({ error: "Not found" });

    const body = req.body || {};

    const data = {};
    if (body.reference !== undefined) data.reference = normalizeOptionalString(body.reference);
    if (body.warehouseId !== undefined) data.warehouseId = String(body.warehouseId || "").trim();
    if (body.type !== undefined) {
      const type = String(body.type || "");
      if (!["Increase", "Decrease"].includes(type)) return res.status(400).json({ error: "Invalid type" });
      data.type = type;
    }
    if (body.status !== undefined) {
      const status = String(body.status || "");
      if (!["Draft", "Posted"].includes(status)) return res.status(400).json({ error: "Invalid status" });
      data.status = status;
    }
    if (body.reason !== undefined) data.reason = normalizeOptionalString(body.reason);
    if (body.notes !== undefined) data.notes = normalizeOptionalString(body.notes);

    const lines = body.lines !== undefined ? parseLines(body.lines) : undefined;
    const newStatus = data.status || existing.status;
    const effectiveType = data.type || existing.type;
    const { apply, reverse } = stockTransition(existing.status, newStatus, "Posted");

    const row = await prisma.$transaction(async (tx) => {
      if (reverse) {
        const oldLines = await tx.inventoryAdjustmentLine.findMany({ where: { adjustmentId: id } });
        const reverseDir = existing.type === "Increase" ? -1 : 1;
        await adjustStock(tx, oldLines, reverseDir, existing.warehouseId);
      }

      if (lines) {
        await tx.inventoryAdjustmentLine.deleteMany({ where: { adjustmentId: id } });
      }
      const updated = await tx.inventoryAdjustment.update({
        where: { id },
        data: {
          ...data,
          ...(lines
            ? {
                lines: {
                  create: lines.map((l) => ({ productId: l.productId, quantity: l.quantity })),
                },
              }
            : {}),
        },
        include: {
          warehouse: { select: { id: true, name: true } },
          lines: { include: { product: { select: { id: true, name: true, sku: true } } } },
        },
      });

      if (apply) {
        const applyDir = effectiveType === "Increase" ? 1 : -1;
        await adjustStock(tx, updated.lines, applyDir, updated.warehouseId);
      }
      return updated;
    });

    return res.json(row);
  } catch (err) {
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.delete("/:id", requireAuth, requireActiveBranch, requireInventoryWrite, async (req, res, next) => {
  try {
    const { id } = req.params;
    const row = await prisma.inventoryAdjustment.findFirst({
      where: { id, branchId: req.activeBranchId },
      include: { lines: true },
    });
    if (!row) return res.status(404).json({ error: "Not found" });

    await prisma.$transaction(async (tx) => {
      if (row.status === "Posted") {
        const reverseDir = row.type === "Increase" ? -1 : 1;
        await adjustStock(tx, row.lines, reverseDir, row.warehouseId);
      }
      await tx.inventoryAdjustment.delete({ where: { id } });
    });
    return res.json({ ok: true });
  } catch (err) {
    return next(err);
  }
});

module.exports = router;

