/**
 * Coolify webhook signature verification.
 *
 * Coolify (≥ 4.0.0-beta.300) signs every webhook with HMAC-SHA256 of the
 * raw body using the per-app `webhook_secret`. The signature is sent in
 * the `X-Coolify-Signature-256` header as `sha256=<hex>`.
 *
 * If the per-app secret is not set, Coolify sends the body unsigned. In
 * that case we reject the call: every prod deploy MUST set a secret.
 *
 * Mirrors the pattern in `lib/gitea.ts:verifyWebhookSignature`.
 */

import { timingSafeStringEq } from "@/lib/server/timing-safe";

export async function verifyCoolifySignature(
  body: string,
  signatureHeader: string | null,
  secret: string,
): Promise<boolean> {
  if (!secret) return false;
  if (!signatureHeader?.startsWith("sha256=")) return false;

  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );
  const sigBytes = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
  const expected =
    "sha256=" +
    Array.from(new Uint8Array(sigBytes))
      .map((b) => b.toString(16).padStart(2, "0"))
      .join("");

  return timingSafeStringEq(expected, signatureHeader);
}
