// lib/webhook-verify.ts
import crypto from "crypto";
import { safeCompare } from "./crypto";

/**
 * Verifies an inbound mail webhook payload using HMAC-SHA256.
 *
 * Most inbound mail providers (Mailgun, Postmark, SendGrid Inbound Parse,
 * etc.) sign the raw request body (sometimes combined with a timestamp)
 * using a shared signing key. This verifies the standard
 * `timestamp + rawBody` HMAC-SHA256 pattern used by Mailgun-style webhooks.
 *
 * @param rawBody      The exact raw request body string (NOT re-serialized JSON)
 * @param timestamp    Timestamp header/field sent by the provider
 * @param signature    Hex-encoded HMAC signature sent by the provider
 * @param signingKey   Shared secret configured in the provider dashboard
 * @param toleranceSec Max age (seconds) a timestamp is considered valid, replay protection
 */
export function verifyInboundWebhookSignature(
  rawBody: string,
  timestamp: string,
  signature: string,
  signingKey: string,
  toleranceSec = 300
): { valid: boolean; reason?: string } {
  if (!rawBody || !timestamp || !signature || !signingKey) {
    return { valid: false, reason: "Missing required verification fields" };
  }

  // Replay-attack protection: reject stale timestamps
  const ts = Number(timestamp);
  if (!Number.isFinite(ts)) {
    return { valid: false, reason: "Invalid timestamp format" };
  }
  const nowSec = Math.floor(Date.now() / 1000);
  if (Math.abs(nowSec - ts) > toleranceSec) {
    return { valid: false, reason: "Timestamp outside acceptable tolerance window" };
  }

  const expectedSignature = crypto
    .createHmac("sha256", signingKey)
    .update(timestamp + rawBody, "utf8")
    .digest("hex");

  const isValid = safeCompare(expectedSignature, signature);

  return isValid ? { valid: true } : { valid: false, reason: "Signature mismatch" };
}

/**
 * Alternative verifier for providers (e.g. Postmark) that sign just the
 * raw body without a timestamp component. Use only if your provider does
 * not support timestamped signatures, since this offers no replay protection
 * on its own — pair it with an idempotency check (e.g. message-id dedupe).
 */
export function verifyRawBodySignature(
  rawBody: string,
  signature: string,
  signingKey: string
): boolean {
  if (!rawBody || !signature || !signingKey) return false;
  const expected = crypto.createHmac("sha256", signingKey).update(rawBody, "utf8").digest("hex");
  return safeCompare(expected, signature);
}
