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

const router = Router();
router.use(requireAuth, requireActiveBranch);

function mapCategoryRow(c) {
  const { parent, _count, ...rest } = c;
  return {
    ...rest,
    parentName: parent?.name ?? null,
    subcategoryCount: _count?.children ?? 0,
    itemCount: 0,
  };
}

router.get("/", 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 = branchWhere(req);
    if (search) {
      where.OR = [
        { name: { contains: search, mode: "insensitive" } },
      ];
    }

    const [total, rows] = await Promise.all([
      prisma.category.count({ where }),
      prisma.category.findMany({
        where,
        orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
        skip,
        take: limit,
        include: {
          parent: { select: { name: true } },
          _count: { select: { children: true } },
        },
      }),
    ]);

    return res.json({
      data: rows.map((row) => mapCategoryRow(row)),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    });
  } catch (err) {
    return next(err);
  }
});

router.get("/:id", requireInventoryRead, async (req, res, next) => {
  try {
    const row = await prisma.category.findFirst({
      where: branchWhere(req, { id: req.params.id }),
      include: {
        parent: { select: { name: true } },
        _count: { select: { children: true } },
      },
    });
    if (!row) return res.status(404).json({ error: "Not found" });
    return res.json(mapCategoryRow(row));
  } catch (err) {
    return next(err);
  }
});

router.post("/", requireInventoryWrite, async (req, res, next) => {
  try {
    const { name, code, description, color, icon, sortOrder, isActive, parentId, tags } = req.body || {};
    if (!name) return res.status(400).json({ error: "Missing name" });

    if (code) {
      const existing = await prisma.category.findFirst({
        where: branchWhere(req, { code }),
      });
      if (existing) return res.status(409).json({ error: "Category code already exists" });
    }

    if (parentId) {
      const parent = await prisma.category.findFirst({
        where: branchWhere(req, { id: parentId }),
      });
      if (!parent) return res.status(400).json({ error: "Parent category not found" });
    }

    let tagsJson = null;
    if (Array.isArray(tags) && tags.length) {
      const cleaned = tags
        .map((t) => (typeof t === "string" ? t.trim() : String(t).trim()))
        .filter(Boolean);
      tagsJson = cleaned.length ? cleaned : null;
    } else if (typeof tags === "string" && tags.trim()) {
      try {
        const parsed = JSON.parse(tags);
        tagsJson = Array.isArray(parsed) ? parsed.map(String).map((s) => s.trim()).filter(Boolean) : null;
        if (tagsJson && !tagsJson.length) tagsJson = null;
      } catch {
        tagsJson = [tags.trim()];
      }
    }

    const category = await prisma.category.create({
      data: {
        name,
        code: code || null,
        description: description || null,
        color: color || null,
        icon: icon || null,
        sortOrder: Number.isFinite(Number(sortOrder)) ? Number(sortOrder) : 0,
        isActive: typeof isActive === "boolean" ? isActive : true,
        parentId: parentId || null,
        branchId: req.activeBranchId,
        ...(tagsJson ? { tags: tagsJson } : {}),
      },
    });

    return res.status(201).json(category);
  } catch (err) {
    return next(err);
  }
});

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

    const { name, code, description, color, icon, sortOrder, isActive, parentId, tags } = req.body || {};
    if (name !== undefined && !String(name).trim()) {
      return res.status(400).json({ error: "Name cannot be empty" });
    }
    if (parentId && parentId === id) {
      return res.status(400).json({ error: "Category cannot be its own parent" });
    }
    if (parentId) {
      const parent = await prisma.category.findFirst({
        where: branchWhere(req, { id: parentId }),
      });
      if (!parent) return res.status(400).json({ error: "Parent category not found" });
    }

    if (code !== undefined) {
      const nextCode = code && String(code).trim() ? String(code).trim() : null;
      if (nextCode) {
        const dup = await prisma.category.findFirst({
          where: branchWhere(req, { code: nextCode, NOT: { id } }),
        });
        if (dup) return res.status(409).json({ error: "Category code already exists" });
      }
    }

    let tagsJson;
    if (tags !== undefined) {
      if (Array.isArray(tags) && tags.length) {
        const cleaned = tags
          .map((t) => (typeof t === "string" ? t.trim() : String(t).trim()))
          .filter(Boolean);
        tagsJson = cleaned.length ? cleaned : null;
      } else if (typeof tags === "string" && tags.trim()) {
        try {
          const parsed = JSON.parse(tags);
          const cleaned = Array.isArray(parsed) ? parsed.map((s) => String(s).trim()).filter(Boolean) : null;
          tagsJson = cleaned && cleaned.length ? cleaned : null;
        } catch {
          tagsJson = [tags.trim()];
        }
      } else {
        tagsJson = null;
      }
    }

    const data = {};
    if (name !== undefined) data.name = String(name).trim();
    if (code !== undefined) data.code = code && String(code).trim() ? String(code).trim() : null;
    if (description !== undefined) {
      data.description = description && String(description).trim() ? String(description).trim() : null;
    }
    if (color !== undefined) data.color = color && String(color).trim() ? String(color).trim() : null;
    if (icon !== undefined) data.icon = icon && String(icon).trim() ? String(icon).trim() : null;
    if (sortOrder !== undefined) {
      data.sortOrder = Number.isFinite(Number(sortOrder)) ? Number(sortOrder) : 0;
    }
    if (isActive !== undefined) data.isActive = Boolean(isActive);
    if (parentId !== undefined) data.parentId = parentId || null;
    if (tagsJson !== undefined) data.tags = tagsJson;

    const category = await prisma.category.update({ where: { id }, data });
    return res.json(category);
  } catch (err) {
    return next(err);
  }
});

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

module.exports = router;
