import { getServerSession } from "next-auth";
import { SafetyNotes, insertAuditLog } from "./db";

type Permission =
  | "authifier"
  | "publish_message"
  | "chat_message"
  | `accounts${
      | ""
      | `/fetch${"" | "/by-id" | "/by-email"}`
      | `/update${"" | "/email" | "/mfa"}`
      | "/disable"
      | "/restore"
      | `/deletion${"" | "/queue" | "/cancel"}`}`
  | `bots${
      | ""
      | `/fetch${"" | "/by-id" | "/by-user"}`
      | `/update${"" | "/discoverability"}`}`
  | `channels${"" | `/fetch${"" | "/by-id" | "/by-server" | "/dm" | "/invites"}` | `/create${"" | "/dm" | "/invites"}` | `/update${"" | "/invites"}`}`
  | `messages${"" | `/fetch${"" | "/by-id" | "/by-user"}`}`
  | `reports${
      | ""
      | `/fetch${
          | ""
          | "/by-id"
          | "/open"
          | `/related${"" | "/by-content" | "/by-user" | "/against-user"}`
          | `/snapshots${"" | "/by-report" | "/by-user"}`}`
      | `/update${
          | ""
          | "/notes"
          | "/resolve"
          | "/reject"
          | "/reopen"
          | `/bulk-close${"" | "/by-user"}`}`}`
  | `sessions${"" | `/fetch${"" | "/by-account-id"}`}`
  | `servers${
      | ""
      | `/fetch${"" | "/by-id"}`
      | `/update${"" | "/flags" | "/discoverability" | "/owner" | "/add-member"}`}`
  | `users${
      | ""
      | `/fetch${
          | ""
          | "/by-id"
          | "/memberships"
          | "/strikes"
          | "/notices"
          | "/relations"}`
      | `/create${"" | "/alert" | "/strike"}`
      | `/update${"" | "/badges"}`
      | `/action${"" | "/unsuspend" | "/suspend" | "/wipe" | "/ban" | "/wipe-profile"}`}`
  | `safety_notes${
      | ""
      | `/fetch${"" | `/${SafetyNotes["_id"]["type"]}`}`
      | `/update${"" | `/${SafetyNotes["_id"]["type"]}`}`}`;

const PermissionSets = {
  // Admin
  admin: [
    "authifier",
    "publish_message",
    "chat_message",
    "accounts",
    "bots",
    "channels",
    "messages",
    "reports",
    "sessions",
    "servers",
    "users",
    "safety_notes",
  ] as Permission[],

  // View open reports
  "view-open-reports": [
    "users/fetch/by-id",
    "reports/fetch/open",
    "reports/fetch/by-id",
    "reports/fetch/related",
    "reports/fetch/snapshots/by-report",
  ] as Permission[],

  // Edit reports
  "edit-reports": [
    "reports/update/notes",
    "reports/update/resolve",
    "reports/update/reject",
    "reports/update/reopen",
  ] as Permission[],

  // Revolt Discover
  "revolt-discover": [
    "servers/fetch/by-id",
    "servers/update/discoverability",
    "servers/update/flags",

    "bots/fetch/by-id",
    "bots/update/discoverability",

    "safety_notes/fetch/global",
    "safety_notes/fetch/server",
    "safety_notes/fetch/user",
    "safety_notes/update/server",
    "safety_notes/update/user",
  ] as Permission[],

  // User support
  "user-support": [
    "users/fetch/by-id",
    "users/fetch/strikes",
    "users/fetch/notices",
    "users/update/badges",

    "servers/update/owner",
    "servers/update/add-member",

    "accounts/fetch/by-id",
    "accounts/fetch/by-email",
    "accounts/disable",
    "accounts/restore",
    "accounts/deletion/queue",
    "accounts/deletion/cancel",
    "accounts/update/email",
    "accounts/update/mfa",

    "channels/update/invites",
    "channels/fetch/invites",

    "safety_notes/fetch",
    "safety_notes/update",
  ] as Permission[],

  // Moderate users
  "moderate-users": [
    "users/fetch/by-id",
    "users/fetch/strikes",
    "users/fetch/notices",

    "bots/fetch/by-user",
    // "messages/fetch/by-user",
    // "users/fetch/memberships",
    "servers/fetch",

    "messages/fetch/by-id",
    "channels/fetch/by-id",
    "channels/fetch/dm",
    "channels/fetch/invites",
    "channels/create/dm",

    "reports/fetch/related/by-user",
    "reports/fetch/related/by-content",
    "reports/fetch/related/against-user",
    "reports/update/bulk-close/by-user",

    "users/create/alert",
    "users/create/strike",
    "users/action/suspend",
    "users/action/wipe",
    "users/action/wipe-profile",
    "users/action/ban",
    "users/action/unsuspend",
    "accounts/disable",
    "accounts/restore",

    "publish_message",
    "chat_message",

    "safety_notes/fetch",
    "safety_notes/update",
  ] as Permission[],
};

const Roles = {
  moderator: [
    ...PermissionSets["view-open-reports"],
    ...PermissionSets["edit-reports"],
    ...PermissionSets["moderate-users"],
  ],
  "user-support": [...PermissionSets["user-support"]],
  "revolt-discover": [...PermissionSets["revolt-discover"]],
  admin: [...PermissionSets["admin"]],
};

const ACL: Record<string, Set<Permission>> = {
  "insert@revolt.chat": new Set([
    ...Roles["moderator"],
    ...Roles["revolt-discover"],
    ...Roles["user-support"],
  ] as Permission[]),
  "lea@janderedev.xyz": new Set([
    ...Roles["moderator"],
    ...Roles["revolt-discover"],
    ...Roles["user-support"],
  ] as Permission[]),
  "infi@infi.sh": new Set([
    ...Roles["moderator"],
    ...Roles["revolt-discover"],
    ...Roles["user-support"],
  ] as Permission[]),
  "beartechtalks@gmail.com": new Set([
    ...Roles["moderator"],
    ...Roles["revolt-discover"],
    ...Roles["user-support"],
  ] as Permission[]),
  "me@zomatree.live": new Set([
    ...Roles["moderator"],
    ...Roles["revolt-discover"],
    ...Roles["user-support"],
  ] as Permission[]),
};

function hasPermission(email: string, permission: Permission) {
  if (process.env.BYPASS_ACL) return true;
  if (!ACL[email]) throw `user is not registered in system: ${email}`;

  const segments = permission.split("/");
  while (segments.length) {
    if (ACL[email].has(segments.join("/") as Permission)) {
      return true;
    }

    segments.pop();
  }

  return false;
}

export async function hasPermissionFromSession(permission: Permission) {
  const session = await getServerSession();
  if (!session?.user?.email) throw "Not authenticated.";
  return hasPermission(session.user.email, permission);
}

export async function checkPermission(
  permission: Permission,
  context: any,
  args?: any
) {
  if (!(await hasPermissionFromSession(permission)))
    throw `Missing permission ${permission}`;

  await insertAuditLog(permission, context, args);
}
