/**
 * pipeline/callback-delivery.ts
 *
 * Delivers evaluation results to a callback URL with HMAC signature.
 * Used by the API-triggered pipeline flow: after the report is published
 * to the Content Lake (system of record), the result is also POSTed
 * to the caller's callback URL.
 *
 * Fire-and-forget with 3 retries and exponential backoff. If callback
 * fails, log a warning — the result is still in the Content Lake.
 *
 * @see docs/design-docs/api-service-gateway.md
 */
import { createHmac } from "crypto";
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const MAX_RETRIES = 3;
const BASE_DELAY_MS = 1000;
const CALLBACK_TIMEOUT_MS = 10_000;
// ---------------------------------------------------------------------------
// HMAC Signature
// ---------------------------------------------------------------------------
/**
 * Generate an HMAC-SHA256 signature for the callback payload.
 *
 * The signature is computed over the raw JSON body using the callback URL
 * as the signing key. This allows the receiver to verify the request
 * originated from AILF without requiring a pre-shared secret.
 *
 * The receiver should:
 * 1. Read the raw request body
 * 2. Compute HMAC-SHA256(body, callbackUrl)
 * 3. Compare with the X-AILF-Signature header
 */
export function computeSignature(body, signingKey) {
    return createHmac("sha256", signingKey).update(body).digest("hex");
}
// ---------------------------------------------------------------------------
// Delivery
// ---------------------------------------------------------------------------
/**
 * Deliver evaluation results to a callback URL.
 *
 * POSTs the payload as JSON with:
 * - Content-Type: application/json
 * - X-AILF-Signature: HMAC-SHA256 signature
 * - Any custom headers from the callback config
 *
 * Retries up to 3 times with exponential backoff (1s → 2s → 4s).
 * Returns success/failure status — never throws.
 */
export async function deliverCallback(callback, payload) {
    const body = JSON.stringify(payload);
    const signature = computeSignature(body, callback.url);
    let lastError;
    let attempts = 0;
    for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
        attempts = attempt + 1;
        try {
            const controller = new AbortController();
            const timeout = setTimeout(() => controller.abort(), CALLBACK_TIMEOUT_MS);
            const response = await fetch(callback.url, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-AILF-Signature": signature,
                    ...(callback.headers ?? {}),
                },
                body,
                signal: controller.signal,
            });
            clearTimeout(timeout);
            if (response.ok) {
                return { ok: true, attempts };
            }
            lastError = `HTTP ${response.status}: ${response.statusText}`;
        }
        catch (err) {
            lastError = err instanceof Error ? err.message : String(err);
        }
        // Don't delay after the last attempt
        if (attempt < MAX_RETRIES) {
            const delayMs = BASE_DELAY_MS * Math.pow(2, attempt);
            await new Promise((resolve) => setTimeout(resolve, delayMs));
        }
    }
    return { ok: false, error: lastError, attempts };
}
