const PROFILE_RANK = new Map([
  [":read-only", 0],
  ["read-only", 0],
  [":workspace", 1],
  ["workspace-write", 1],
  [":danger-full-access", 2],
  ["danger-full-access", 2]
]);
const APPROVAL_RANK = new Map([
  ["untrusted", 0],
  ["on-failure", 1],
  ["on-request", 2],
  ["never", 3]
]);
const SANDBOX_RANK = new Map([
  ["read-only", 0],
  ["workspace-write", 1],
  ["danger-full-access", 2]
]);

export const fullPermissionCeiling = Object.freeze({
  permissionProfile: ":danger-full-access",
  approvalPolicy: "never"
});

export function validatePermissionCeiling(value) {
  if (value === undefined) return;
  if (!value || typeof value !== "object" || Array.isArray(value)) throw configurationInvalid();
  if (!PROFILE_RANK.has(value.permissionProfile) || !value.permissionProfile.startsWith(":")) {
    throw configurationInvalid();
  }
  if (!APPROVAL_RANK.has(value.approvalPolicy)) throw configurationInvalid();
}

export function assertMessagePermissionsAllowed(options = {}, ceiling = fullPermissionCeiling) {
  const local = resolvedCeiling(ceiling);
  assertKnownAndWithin(options, "permissionProfile", PROFILE_RANK, PROFILE_RANK.get(local.permissionProfile));
  assertKnownAndWithin(options, "approvalPolicy", APPROVAL_RANK, APPROVAL_RANK.get(local.approvalPolicy));
  assertKnownAndWithin(options, "sandbox", SANDBOX_RANK, PROFILE_RANK.get(local.permissionProfile));
}

export function publicPermissionProfilesWithinCeiling(profiles = [], ceiling = fullPermissionCeiling) {
  const maximum = PROFILE_RANK.get(resolvedCeiling(ceiling).permissionProfile);
  return profiles.filter((profile) => {
    const rank = PROFILE_RANK.get(profile?.id);
    return rank !== undefined && rank <= maximum;
  });
}

function resolvedCeiling(value) {
  return value === undefined ? fullPermissionCeiling : value;
}

function assertKnownAndWithin(options, key, ranks, maximum) {
  if (!Object.hasOwn(options ?? {}, key)) return;
  const rank = ranks.get(options[key]);
  if (rank === undefined) throw permissionError("permission_not_allowed");
  if (rank > maximum) throw permissionError("permission_ceiling_exceeded");
}

function permissionError(code) {
  const error = new Error(code);
  error.status = 400;
  error.safeLogCode = code;
  return error;
}

function configurationInvalid() {
  return new Error("configuration_invalid");
}
