const { Router } = require("express");
const prisma = require("../prisma");
const { requireAuth } = require("../middleware/auth");
const { requirePosRead, requirePosWrite } = require("../middleware/perms");
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: "Expense reference already exists" });
  }
  return null;
}

function randomRef() {
  const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  let out = "EXP-";
  for (let i = 0; i < 8; i += 1) out += alphabet[Math.floor(Math.random() * alphabet.length)];
  return out;
}

router.get("/", requireAuth, requireActiveBranch, requirePosRead, 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" } },
        { title: { contains: search, mode: "insensitive" } },
      ];
    }

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

    const data = rows.map((r) => ({
      id: r.id,
      reference: r.reference,
      status: r.status,
      expenseDate: r.expenseDate,
      title: r.title,
      amount: r.amount,
      categoryId: r.categoryId,
      categoryName: r.category?.name || null,
      warehouseId: r.warehouseId,
      warehouseName: r.warehouse?.name || null,
      paymentMethodId: r.paymentMethodId,
      paymentMethodName: r.paymentMethod?.name || null,
      createdAt: r.createdAt,
      updatedAt: r.updatedAt,
    }));

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

router.get("/summary", requireAuth, requireActiveBranch, requirePosRead, async (req, res, next) => {
  try {
    const branchId = req.activeBranchId;
    const where = { branchId };

    const [totalCount, amountAgg, byStatus] = await Promise.all([
      prisma.expense.count({ where }),
      prisma.expense.aggregate({
        where,
        _sum: { amount: true },
      }),
      prisma.expense.groupBy({
        by: ["status"],
        where,
        _count: { _all: true },
        _sum: { amount: true },
      }),
    ]);

    const statusCounts = Object.fromEntries(
      byStatus.map((row) => [row.status, row._count._all])
    );
    const statusAmounts = Object.fromEntries(
      byStatus.map((row) => [row.status, row._sum.amount || 0])
    );

    const recordedTotal = amountAgg._sum.amount || 0;
    const approvedTotal = (statusAmounts.Approved || 0) + (statusAmounts.Paid || 0);

    return res.json({
      totalCount,
      totalAmount: recordedTotal,
      approvedAmount: approvedTotal,
      draftCount: statusCounts.Draft || 0,
      approvedCount: statusCounts.Approved || 0,
      paidCount: statusCounts.Paid || 0,
      cancelledCount: statusCounts.Cancelled || 0,
    });
  } catch (err) {
    return next(err);
  }
});

router.get("/generate-reference", requireAuth, requirePosRead, async (req, res, next) => {
  try {
    for (let i = 0; i < 20; i += 1) {
      const reference = randomRef();
      const exists = await prisma.expense.findUnique({ where: { reference } });
      if (!exists) return res.json({ reference });
    }
    return res.status(503).json({ error: "Could not generate expense reference. Try again." });
  } catch (err) {
    return next(err);
  }
});

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

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

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

    const amount = Number(body.amount) || 0;
    if (!(amount >= 0)) return res.status(400).json({ error: "Invalid amount" });

    const expenseDate = body.expenseDate ? new Date(body.expenseDate) : new Date();

    const row = await prisma.expense.create({
      data: {
        reference,
        categoryId: normalizeOptionalString(body.categoryId),
        warehouseId: normalizeOptionalString(body.warehouseId),
        paymentMethodId: normalizeOptionalString(body.paymentMethodId),
        branchId: req.activeBranchId,
        status,
        expenseDate: Number.isNaN(expenseDate.getTime()) ? new Date() : expenseDate,
        title: normalizeOptionalString(body.title),
        notes: normalizeOptionalString(body.notes),
        amount,
      },
      include: {
        category: { select: { id: true, name: true } },
        warehouse: { select: { id: true, name: true } },
        paymentMethod: { select: { id: true, name: true } },
      },
    });
    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, requirePosWrite, async (req, res, next) => {
  try {
    const { id } = req.params;
    const existing = await prisma.expense.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) {
      const reference = String(body.reference || "").trim();
      if (!reference) return res.status(400).json({ error: "Reference cannot be empty" });
      data.reference = reference;
    }
    if (body.categoryId !== undefined) data.categoryId = normalizeOptionalString(body.categoryId);
    if (body.warehouseId !== undefined) data.warehouseId = normalizeOptionalString(body.warehouseId);
    if (body.paymentMethodId !== undefined) data.paymentMethodId = normalizeOptionalString(body.paymentMethodId);
    if (body.status !== undefined) {
      const status = String(body.status || "");
      if (!["Draft", "Approved", "Paid", "Cancelled"].includes(status)) return res.status(400).json({ error: "Invalid status" });
      data.status = status;
    }
    if (body.expenseDate !== undefined) {
      const d = body.expenseDate ? new Date(body.expenseDate) : null;
      data.expenseDate = d && !Number.isNaN(d.getTime()) ? d : existing.expenseDate;
    }
    if (body.title !== undefined) data.title = normalizeOptionalString(body.title);
    if (body.notes !== undefined) data.notes = normalizeOptionalString(body.notes);
    if (body.amount !== undefined) {
      const amount = Number(body.amount);
      if (!Number.isFinite(amount) || amount < 0) return res.status(400).json({ error: "Invalid amount" });
      data.amount = amount;
    }

    const row = await prisma.expense.update({
      where: { id },
      data,
      include: {
        category: { select: { id: true, name: true } },
        warehouse: { select: { id: true, name: true } },
        paymentMethod: { select: { id: true, name: true } },
      },
    });
    return res.json(row);
  } catch (err) {
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

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

module.exports = router;

