const { Router } = require("express");
const prisma = require("../prisma");
const { requireAuth } = require("../middleware/auth");
const {
  visibleRolesWhere,
  requireUserManager,
  assertCanManageRole,
  assertRoleNameAllowed,
  assertCanDeleteRole,
  isAdminOnly,
  SUPER_ADMIN_ROLE,
} = require("../lib/rbac");

const router = Router();

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

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

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

    return res.json({
      data: rows.map((r) => ({
        id: r.id,
        name: r.name,
        userCount: r._count?.users || 0,
        permissionCount: r._count?.permissions || 0,
        createdAt: r.createdAt,
        updatedAt: r.updatedAt,
      })),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    });
  } catch (err) {
    return next(err);
  }
});

router.get("/:id", requireAuth, requireUserManager, async (req, res, next) => {
  try {
    const role = await assertCanManageRole(prisma, req, req.params.id);
    const row = await prisma.role.findUnique({
      where: { id: role.id },
      include: {
        permissions: { include: { permission: true } },
        users: {
          include: {
            user: {
              select: {
                id: true,
                email: true,
                fullName: true,
                username: true,
                isApproved: true,
                roles: { select: { role: { select: { name: true } } } },
              },
            },
          },
        },
      },
    });
    if (!row) return res.status(404).json({ error: "Not found" });

    let users = row.users.map((u) => u.user);
    if (isAdminOnly(req)) {
      users = users.filter(
        (u) => !u.roles.some((r) => r.role.name === SUPER_ADMIN_ROLE)
      );
    }

    return res.json({
      id: row.id,
      name: row.name,
      createdAt: row.createdAt,
      updatedAt: row.updatedAt,
      permissionIds: row.permissions.map((p) => p.permissionId),
      permissions: row.permissions.map((p) => p.permission),
      users: users.map(({ roles, ...rest }) => rest),
    });
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    return next(err);
  }
});

router.post("/", requireAuth, requireUserManager, async (req, res, next) => {
  try {
    const body = req.body || {};
    const name = assertRoleNameAllowed(req, body.name);
    if (!name) return res.status(400).json({ error: "Missing name" });
    const row = await prisma.role.create({ data: { name } });
    return res.status(201).json(row);
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.patch("/:id", requireAuth, requireUserManager, async (req, res, next) => {
  try {
    const { id } = req.params;
    const existing = await assertCanManageRole(prisma, req, id);
    const body = req.body || {};
    const data = {};
    if (body.name !== undefined) {
      const name = assertRoleNameAllowed(req, body.name);
      if (!name) return res.status(400).json({ error: "Name cannot be empty" });
      if (existing.name === SUPER_ADMIN_ROLE && name !== SUPER_ADMIN_ROLE) {
        return res.status(403).json({ error: "The Super Admin role cannot be renamed" });
      }
      data.name = name;
    }
    const row = await prisma.role.update({ where: { id }, data });
    return res.json(row);
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.put("/:id/permissions", requireAuth, requireUserManager, async (req, res, next) => {
  try {
    const { id } = req.params;
    await assertCanManageRole(prisma, req, id);

    const { permissionIds } = req.body || {};
    if (!Array.isArray(permissionIds)) return res.status(400).json({ error: "permissionIds must be an array" });

    const unique = Array.from(new Set(permissionIds.map((x) => String(x).trim()).filter(Boolean)));
    const perms = unique.length
      ? await prisma.permission.findMany({ where: { id: { in: unique } }, select: { id: true } })
      : [];
    if (perms.length !== unique.length) return res.status(400).json({ error: "One or more permissions are invalid" });

    const updated = await prisma.$transaction(async (tx) => {
      await tx.rolePermission.deleteMany({ where: { roleId: id } });
      if (unique.length) {
        await tx.rolePermission.createMany({
          data: unique.map((permissionId) => ({ roleId: id, permissionId })),
          skipDuplicates: true,
        });
      }
      return tx.role.findUnique({
        where: { id },
        include: { permissions: { include: { permission: true } } },
      });
    });
    return res.json({
      id: updated.id,
      name: updated.name,
      permissionIds: updated.permissions.map((p) => p.permissionId),
      permissions: updated.permissions.map((p) => p.permission),
    });
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    return next(err);
  }
});

router.delete("/:id", requireAuth, requireUserManager, async (req, res, next) => {
  try {
    const { id } = req.params;
    const row = await assertCanManageRole(prisma, req, id);
    assertCanDeleteRole(req, row.name);
    await prisma.role.delete({ where: { id } });
    return res.json({ ok: true });
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    return next(err);
  }
});

module.exports = router;
