import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto";

export type PassAad = {
  pass_no: string;
  aadhaar_last4: string;
};

function resolveKey(): Buffer {
  const raw = process.env.PASS_ENCRYPTION_KEY;
  if (raw && raw.length > 0) {
    return keyFromMaterial(raw);
  }
  if (process.env.NODE_ENV === "production") {
    throw new Error("PASS_ENCRYPTION_KEY is required in production");
  }
  const fallback = process.env.JWT_SECRET || "kumbh-dev-secret";
  return createHash("sha256").update(`pass-dev:${fallback}`).digest();
}

function keyFromMaterial(material: string): Buffer {
  const trimmed = material.trim();
  // 64 hex chars = 32 bytes
  if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {
    return Buffer.from(trimmed, "hex");
  }
  const utf8 = Buffer.from(trimmed, "utf8");
  if (utf8.length === 32) return utf8;
  if (utf8.length > 32) return utf8.subarray(0, 32);
  return createHash("sha256").update(utf8).digest();
}

function aadBuffer(aad: PassAad): Buffer {
  return Buffer.from(`${aad.pass_no}|${aad.aadhaar_last4}`, "utf8");
}

/**
 * Seal pass payload as `v1:<iv_b64>:<ciphertext_b64>:<tag_b64>`.
 */
export function sealPassPayload(
  obj: Record<string, unknown>,
  aad: PassAad,
): string {
  const key = resolveKey();
  const iv = randomBytes(12);
  const cipher = createCipheriv("aes-256-gcm", key, iv);
  cipher.setAAD(aadBuffer(aad));
  const plaintext = Buffer.from(JSON.stringify(obj), "utf8");
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
  const tag = cipher.getAuthTag();
  return [
    "v1",
    iv.toString("base64"),
    ciphertext.toString("base64"),
    tag.toString("base64"),
  ].join(":");
}

/**
 * Open a sealed pass payload. Caller should supply the same AAD used at seal time.
 * Throws if ciphertext is invalid / tag mismatch.
 */
export function openPassPayload(
  sealed: string,
  aad?: PassAad,
): Record<string, unknown> {
  const parts = sealed.split(":");
  if (parts.length !== 4 || parts[0] !== "v1") {
    throw new Error("Unsupported sealed payload format");
  }
  const [, ivB64, ctB64, tagB64] = parts;
  const key = resolveKey();
  const iv = Buffer.from(ivB64, "base64");
  const ciphertext = Buffer.from(ctB64, "base64");
  const tag = Buffer.from(tagB64, "base64");
  const decipher = createDecipheriv("aes-256-gcm", key, iv);
  if (aad) {
    decipher.setAAD(aadBuffer(aad));
  }
  decipher.setAuthTag(tag);
  const plaintext = Buffer.concat([
    decipher.update(ciphertext),
    decipher.final(),
  ]);
  return JSON.parse(plaintext.toString("utf8")) as Record<string, unknown>;
}

/** True when payload uses the v1 sealed format. */
export function isSealedPassPayload(payload: string): boolean {
  return payload.startsWith("v1:");
}
