// PAM domain helpers shared by API routes + UI loaders.
//
// Boundary: anything that talks to admin.privilege_* goes here so
// the route handlers stay thin and the cache invalidation point
// stays in one place.

import { prisma } from "@/database/prisma";
import { invalidateAuthzCache } from "@/lib/authz";
import crypto from "node:crypto";

export type PamEvent =
  | "requested"
  | "approved-auto"
  | "approved-manual"
  | "denied"
  | "activated"
  | "revoked"
  | "expired"
  | "used";

export interface AuditInput {
  event: PamEvent;
  gscsid: string;
  roleName: string;
  grantId?: string | null;
  actorGscsid?: string | null;
  actorEmail?: string | null;
  detail?: Record<string, unknown> | null;
}

export async function recordAudit(input: AuditInput): Promise<void> {
  await prisma.privilegeAudit.create({
    data: {
      event: input.event,
      gscsid: input.gscsid,
      roleName: input.roleName,
      grantId: input.grantId ?? null,
      actorGscsid: input.actorGscsid ?? null,
      actorEmail: input.actorEmail ?? null,
      detail: (input.detail as never) ?? undefined,
    },
  });
}

export function generateApprovalToken(): string {
  return crypto.randomBytes(24).toString("base64url");
}

/**
 * Pulls active, non-expired grants for a user. Used by the
 * /api/pam/active and the top-bar widget loader.
 */
export async function listActiveGrants(gscsid: string) {
  return prisma.privilegeGrant.findMany({
    where: {
      gscsid,
      status: "active",
      expiresAt: { gt: new Date() },
    },
    orderBy: { expiresAt: "asc" },
    select: {
      id: true,
      roleName: true,
      grantedAt: true,
      expiresAt: true,
      justification: true,
    },
  });
}

/**
 * Pending (manual-mode, awaiting email approval) grants for a user.
 */
export async function listPendingGrants(gscsid: string) {
  return prisma.privilegeGrant.findMany({
    where: { gscsid, status: "pending" },
    orderBy: { requestedAt: "desc" },
    select: {
      id: true,
      roleName: true,
      requestedAt: true,
      expiresAt: true,
      justification: true,
    },
  });
}

/**
 * Recent audit entries for a user.
 */
export async function listAudit(gscsid: string, limit = 50) {
  return prisma.privilegeAudit.findMany({
    where: { gscsid },
    orderBy: { ts: "desc" },
    take: limit,
    select: {
      id: true,
      ts: true,
      event: true,
      roleName: true,
      grantId: true,
      actorGscsid: true,
      actorEmail: true,
      detail: true,
    },
  });
}

export async function getPolicy(roleName: string) {
  return prisma.privilegePolicy.findUnique({ where: { roleName } });
}

/**
 * Revoke an active grant. Caller must already have established
 * authorization to do so (self-revoke or admin override).
 */
export async function revokeGrant(
  grantId: string,
  byGscsid: string,
  reason: string | null,
): Promise<void> {
  const grant = await prisma.privilegeGrant.findUnique({
    where: { id: grantId },
    select: { id: true, gscsid: true, roleName: true, status: true },
  });
  if (!grant) return;
  if (grant.status !== "active" && grant.status !== "pending") return;

  await prisma.privilegeGrant.update({
    where: { id: grantId },
    data: {
      status: "revoked",
      revokedAt: new Date(),
      revokedBy: byGscsid,
      revokeReason: reason,
    },
  });
  await recordAudit({
    event: "revoked",
    gscsid: grant.gscsid,
    roleName: grant.roleName,
    grantId: grant.id,
    actorGscsid: byGscsid,
    detail: reason ? { reason } : undefined,
  });
  invalidateAuthzCache(grant.gscsid, grant.roleName);
}
