const { Router } = require("express");
const prisma = require("../prisma");
const { requireAuth } = require("../middleware/auth");
const { requireInventoryRead, requireInventoryWrite } = require("../middleware/perms");
const { auditFromReq } = require("../lib/audit");
const { notDeleted } = require("../lib/softDelete");
const { syncProductQuantity } = require("../lib/inventory");
const { requireActiveBranch } = require("../middleware/branch");

const router = Router();

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

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: "SKU already exists" });
  }
  return null;
}

function toFloatOrNull(v) {
  if (v === undefined) return undefined;
  if (v === null || v === "") return null;
  const n = Number(v);
  return Number.isFinite(n) ? n : null;
}

function validateSellingPrice(value, { required = false } = {}) {
  if (value === undefined) {
    return required ? "Missing selling price" : null;
  }
  if (value === null || value === "") {
    return required ? "Missing selling price" : null;
  }
  const n = Number(value);
  if (!Number.isFinite(n) || n <= 0) {
    return "Selling price must be greater than 0";
  }
  return null;
}

function buildProductData(body, { allowNulls = false } = {}) {
  const data = {};

  if (body.name !== undefined) data.name = String(body.name).trim();
  if (body.sku !== undefined) data.sku = String(body.sku).trim();

  const strFields = [
    "barcode",
    "description",
    "productType",
  ];
  for (const k of strFields) {
    if (body[k] !== undefined) data[k] = normalizeOptionalString(body[k]);
  }

  const fkFields = ["categoryId", "unitId", "brandId", "warehouseId", "salesTaxId", "purchaseTaxId"];
  for (const k of fkFields) {
    if (body[k] !== undefined) {
      const v = normalizeOptionalString(body[k]);
      data[k] = v;
    }
  }

  if (body.isReturnable !== undefined) data.isReturnable = Boolean(body.isReturnable);
  if (body.sortOrder !== undefined) data.sortOrder = Number.isFinite(Number(body.sortOrder)) ? Number(body.sortOrder) : 0;
  if (body.isActive !== undefined) data.isActive = Boolean(body.isActive);

  if (body.sellingPrice !== undefined) data.sellingPrice = toFloatOrNull(body.sellingPrice);
  if (body.costPrice !== undefined) data.costPrice = toFloatOrNull(body.costPrice);

  if (body.expiryDate !== undefined) {
    if (body.expiryDate === null || body.expiryDate === "") {
      data.expiryDate = null;
    } else {
      const d = new Date(body.expiryDate);
      if (!Number.isNaN(d.getTime())) data.expiryDate = d;
    }
  }

  if (body.variationIds !== undefined) {
    if (body.variationIds === null) {
      data.variationIds = null;
    } else if (Array.isArray(body.variationIds)) {
      data.variationIds = body.variationIds.map(String).map((s) => s.trim()).filter(Boolean);
    } else {
      data.variationIds = null;
    }
  }

  if (body.quantity !== undefined) {
    const n = Number(body.quantity);
    data.quantity = Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
  }

  if (body.images !== undefined) {
    if (body.images === null) {
      data.images = null;
    } else if (Array.isArray(body.images)) {
      data.images = body.images.map(String).map((s) => s.trim()).filter(Boolean);
    } else {
      data.images = null;
    }
  }

  if (!allowNulls) {
    // For POST: remove null/undefined optional fields to let defaults apply.
    for (const [k, v] of Object.entries(data)) {
      if (v === undefined) delete data[k];
      if (v === null && k !== "sellingPrice" && k !== "costPrice") delete data[k];
    }
  }

  return data;
}

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 warehouseId = (req.query.warehouseId || "").trim();
    const where = { ...notDeleted, branchId: req.activeBranchId };
    if (search) {
      where.OR = [
        { name: { contains: search, mode: "insensitive" } },
        { sku: { contains: search, mode: "insensitive" } },
        { barcode: { contains: search, mode: "insensitive" } },
      ];
    }

    const [total, rows] = await Promise.all([
      prisma.product.count({ where }),
      prisma.product.findMany({
        where,
        orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
        skip,
        take: limit,
        include: {
          category: { select: { id: true, name: true } },
          unit: { select: { id: true, name: true, symbol: true } },
          brand: { select: { id: true, name: true } },
          warehouse: { select: { id: true, name: true } },
          ...(warehouseId
            ? { warehouseStocks: { where: { warehouseId }, select: { quantity: true, warehouseId: true } } }
            : {}),
        },
      }),
    ]);

    const data = warehouseId
      ? rows.map((r) => {
          const ws = r.warehouseStocks?.[0];
          return { ...r, warehouseQuantity: ws?.quantity ?? 0, warehouseStocks: undefined };
        })
      : rows;

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

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

router.get("/:id", requireAuth, requireActiveBranch, requireInventoryRead, async (req, res, next) => {
  try {
    const row = await prisma.product.findFirst({
      where: { id: req.params.id, ...notDeleted, branchId: req.activeBranchId },
      include: {
        category: { select: { id: true, name: true } },
        unit: { select: { id: true, name: true, symbol: true } },
        brand: { select: { id: true, name: true } },
        warehouse: { select: { id: true, name: true } },
        warehouseStocks: {
          include: { warehouse: { 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, requireInventoryWrite, async (req, res, next) => {
  try {
    const body = req.body || {};
    if (!body.name || !String(body.name).trim()) return res.status(400).json({ error: "Missing name" });
    if (!body.sku || !String(body.sku).trim()) return res.status(400).json({ error: "Missing sku" });
    const sellingPriceError = validateSellingPrice(body.sellingPrice, { required: true });
    if (sellingPriceError) return res.status(400).json({ error: sellingPriceError });

    const data = buildProductData(body, { allowNulls: false });
    data.branchId = req.activeBranchId;
    const warehouseId = data.warehouseId;
    const initialQty = data.quantity ?? 0;
    const row = await prisma.$transaction(async (tx) => {
      const created = await tx.product.create({ data });
      if (warehouseId && initialQty > 0) {
        await tx.warehouseStock.upsert({
          where: { warehouseId_productId: { warehouseId, productId: created.id } },
          create: { warehouseId, productId: created.id, quantity: initialQty },
          update: { quantity: initialQty },
        });
        await syncProductQuantity(tx, created.id);
      }
      return created;
    });
    auditFromReq(req, { action: "create", entityType: "Product", entityId: row.id, summary: row.name });
    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.product.findFirst({ where: { id, ...notDeleted, branchId: req.activeBranchId } });
    if (!existing) return res.status(404).json({ error: "Not found" });

    const body = req.body || {};
    if (body.name !== undefined && !String(body.name).trim()) {
      return res.status(400).json({ error: "Name cannot be empty" });
    }
    if (body.sku !== undefined && !String(body.sku).trim()) {
      return res.status(400).json({ error: "SKU cannot be empty" });
    }
    if (body.sellingPrice !== undefined) {
      const sellingPriceError = validateSellingPrice(body.sellingPrice, { required: true });
      if (sellingPriceError) return res.status(400).json({ error: sellingPriceError });
    }

    const data = buildProductData(body, { allowNulls: true });
    const row = await prisma.product.update({ where: { id }, data });
    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.product.findFirst({ where: { id, ...notDeleted, branchId: req.activeBranchId } });
    if (!row) return res.status(404).json({ error: "Not found" });
    await prisma.product.update({ where: { id }, data: { deletedAt: new Date() } });
    auditFromReq(req, { action: "delete", entityType: "Product", entityId: id, summary: row.name });
    return res.json({ ok: true });
  } catch (err) {
    return next(err);
  }
});

module.exports = router;

