/**
 * notifications/webhook-adapter.ts
 *
 * The NotificationChannelAdapter for `type: "webhook"`. Resolves a channel's
 * logical name to a real URL from the runner environment (URLs never live in
 * the Content Lake — D0045 / spec Path 1), guards egress, builds the signed
 * envelope, and POSTs. An unregistered logical name is a `skipped` outcome, not
 * a failure, so an unwired subscription is operationally visible.
 */
import { buildWebhookEnvelope, deriveEvents, projectReport, } from "../_vendor/ailf-core/index.js";
import { deliverWebhook } from "../pipeline/callback-delivery.js";
/**
 * Env key for a logical webhook name: `groq-ci-relay` → `AILF_WEBHOOK_URL__GROQ_CI_RELAY`.
 * The normalization is many-to-one (`a.b` and `a-b` both map to `A_B`), so operators
 * must choose distinct logical names that differ after uppercasing and non-alphanumeric collapse.
 */
export function webhookEnvKey(logicalName) {
    return `AILF_WEBHOOK_URL__${logicalName.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
}
/** Resolve a logical name to a URL from an env map (defaults to process.env). */
export function resolveWebhookUrl(logicalName, env = process.env) {
    return env[webhookEnvKey(logicalName)];
}
export class WebhookNotificationAdapter {
    opts;
    type = "webhook";
    constructor(opts) {
        this.opts = opts;
    }
    async deliver(target, content) {
        const { channel } = target;
        if (channel.type !== "webhook") {
            return { reason: "unsupported_channel_type", status: "skipped" };
        }
        if (content.kind !== "report") {
            return { reason: "digest_not_supported", status: "skipped" };
        }
        const url = resolveWebhookUrl(channel.logicalName, this.opts.env);
        if (!url) {
            return {
                reason: `unregistered_webhook:${channel.logicalName}`,
                status: "skipped",
            };
        }
        const derived = deriveEvents(content.report);
        const filter = channel.events;
        const matched = filter && filter.length > 0
            ? derived.filter((e) => filter.includes(e))
            : derived;
        if (matched.length === 0) {
            return { reason: "no_matching_events", status: "skipped" };
        }
        const envelope = buildWebhookEnvelope({
            report: projectReport(content.report),
            events: matched,
            surface: "subscription",
            deliveredAt: new Date().toISOString(),
            summary: content.report.summary,
        });
        const result = await deliverWebhook({ url }, envelope, {
            signingSecret: this.opts.signingSecret,
            resolve: this.opts.resolve,
        });
        return result.ok
            ? { status: "delivered" }
            : { error: result.error ?? "delivery failed", status: "failed" };
    }
}
