/** Role-based access for user / role management (ghost Super Admin). */

const SUPER_ADMIN_ROLE = "Super Admin";
const ADMIN_ROLE = "Admin";
const SYSTEM_ROLE_NAMES = [SUPER_ADMIN_ROLE, ADMIN_ROLE];

function rolesOf(req) {
  return req.user?.roles || [];
}

function isSuperAdmin(req) {
  return rolesOf(req).includes(SUPER_ADMIN_ROLE);
}

function isAdmin(req) {
  return rolesOf(req).includes(ADMIN_ROLE);
}

/** Admin or Super Admin — can manage users, roles, permissions. */
function canManageUsers(req) {
  return isSuperAdmin(req) || isAdmin(req);
}

/** Admin only (not Super Admin). */
function isAdminOnly(req) {
  return isAdmin(req) && !isSuperAdmin(req);
}

const hasSuperAdminRole = {
  roles: { some: { role: { name: SUPER_ADMIN_ROLE } } },
};

/** Prisma `where` for listing users visible to the current actor. */
function visibleUsersWhere(req, extra = {}) {
  if (isSuperAdmin(req)) return { ...extra };
  if (isAdminOnly(req)) return { ...extra, NOT: hasSuperAdminRole };
  return { ...extra, id: "__forbidden__" };
}

/** Prisma `where` for listing roles visible to the current actor. */
function visibleRolesWhere(req, extra = {}) {
  if (isSuperAdmin(req)) return { ...extra };
  if (isAdminOnly(req)) return { ...extra, name: { not: SUPER_ADMIN_ROLE } };
  return { ...extra, id: "__forbidden__" };
}

async function getUserRoleNames(prisma, userId) {
  const rows = await prisma.userRole.findMany({
    where: { userId },
    include: { role: { select: { name: true } } },
  });
  return rows.map((r) => r.role.name);
}

function userHasSuperAdminRole(roleNames) {
  return roleNames.includes(SUPER_ADMIN_ROLE);
}

/**
 * Ensure actor may view/manage target user. Returns 404 for hidden Super Admin users.
 */
async function assertCanAccessUser(prisma, req, targetUserId) {
  if (!canManageUsers(req)) {
    const err = new Error("Forbidden");
    err.status = 403;
    throw err;
  }
  if (targetUserId === req.user.sub) return;

  const targetRoles = await getUserRoleNames(prisma, targetUserId);
  if (isAdminOnly(req) && userHasSuperAdminRole(targetRoles)) {
    const err = new Error("User not found");
    err.status = 404;
    throw err;
  }
}

async function assertCanAssignRoleIds(prisma, req, roleIds) {
  if (!roleIds?.length) return;
  const roles = await prisma.role.findMany({
    where: { id: { in: roleIds } },
    select: { id: true, name: true },
  });
  if (roles.length !== roleIds.length) {
    const err = new Error("One or more roles are invalid");
    err.status = 400;
    throw err;
  }
  if (isAdminOnly(req) && roles.some((r) => r.name === SUPER_ADMIN_ROLE)) {
    const err = new Error("You cannot assign the Super Admin role");
    err.status = 403;
    throw err;
  }
}

async function assertCanManageRole(prisma, req, roleId) {
  const role = await prisma.role.findUnique({ where: { id: roleId } });
  if (!role) {
    const err = new Error("Not found");
    err.status = 404;
    throw err;
  }
  if (isAdminOnly(req) && role.name === SUPER_ADMIN_ROLE) {
    const err = new Error("Not found");
    err.status = 404;
    throw err;
  }
  return role;
}

function assertRoleNameAllowed(req, name) {
  const n = String(name || "").trim();
  if (isAdminOnly(req) && n === SUPER_ADMIN_ROLE) {
    const err = new Error("You cannot create or rename a role to Super Admin");
    err.status = 403;
    throw err;
  }
  return n;
}

function assertCanDeleteRole(req, roleName) {
  if (SYSTEM_ROLE_NAMES.includes(roleName)) {
    const err = new Error("System roles cannot be deleted");
    err.status = 403;
    throw err;
  }
  if (isAdminOnly(req) && roleName === SUPER_ADMIN_ROLE) {
    const err = new Error("Not found");
    err.status = 404;
    throw err;
  }
}

function requireUserManager(req, res, next) {
  if (!canManageUsers(req)) {
    return res.status(403).json({ error: "Forbidden" });
  }
  return next();
}

module.exports = {
  SUPER_ADMIN_ROLE,
  ADMIN_ROLE,
  SYSTEM_ROLE_NAMES,
  rolesOf,
  isSuperAdmin,
  isAdmin,
  isAdminOnly,
  canManageUsers,
  visibleUsersWhere,
  visibleRolesWhere,
  getUserRoleNames,
  userHasSuperAdminRole,
  assertCanAccessUser,
  assertCanAssignRoleIds,
  assertCanManageRole,
  assertRoleNameAllowed,
  assertCanDeleteRole,
  requireUserManager,
};
