"use strict";
/**
 * Manifest Push Emitter — customer-side helper that signs a manifest and
 * POSTs it to the receive endpoint.
 *
 * Wraps:
 *   - W2.1 discovery output (AgentManifest)
 *   - signed-manifest helper (Ed25519 + canonical JSON)
 *   - retry with exponential backoff for transient failures (5xx / network)
 *
 * Honors Invariant 10 — push-only, fire-and-forget from the customer side.
 * Local backpressure queue (in-memory) is left to the caller; this helper
 * focuses on the single push lifecycle.
 *
 * @connexum/ai-governance  F-NEW-PRODUCT-AGENT-DISCOVERY-CLI (client push)
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManifestPushEmitter = void 0;
const signed_manifest_js_1 = require("./signed-manifest.js");
const approval_channel_adapters_js_1 = require("./approval-channel-adapters.js");
const DEFAULT_SLEEP = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function parseUrl(url) {
    const u = new URL(url);
    return { hostname: u.hostname, path: u.pathname + u.search };
}
class ManifestPushEmitter {
    options;
    constructor(options) {
        if (!options.orgId || options.orgId.trim() === '') {
            throw new Error('ManifestPushEmitter: orgId is required');
        }
        if (!options.endpoint || options.endpoint.trim() === '') {
            throw new Error('ManifestPushEmitter: endpoint is required');
        }
        this.options = {
            orgId: options.orgId,
            privateKey: options.privateKey,
            endpoint: options.endpoint,
            maxRetries: options.maxRetries ?? 3,
            baseBackoffMs: options.baseBackoffMs ?? 500,
            transport: options.transport ?? approval_channel_adapters_js_1.nodeHttpsTransport,
            sleep: options.sleep ?? DEFAULT_SLEEP,
        };
    }
    /** Sign and push a single manifest. */
    async push(manifest) {
        const signed = (0, signed_manifest_js_1.signManifest)(manifest, this.options.orgId, this.options.privateKey);
        return this.pushSigned(signed);
    }
    /** Push a manifest you have already signed (e.g. signed on a different host). */
    async pushSigned(signed) {
        const body = JSON.stringify(signed);
        const { hostname, path } = parseUrl(this.options.endpoint);
        let lastErr;
        let lastStatus;
        for (let attempt = 0; attempt < this.options.maxRetries + 1; attempt++) {
            try {
                const res = await this.options.transport({
                    method: 'POST',
                    hostname,
                    path,
                    headers: {
                        'content-type': 'application/json',
                        'content-length': Buffer.byteLength(body).toString(),
                        'x-connexum-org': this.options.orgId,
                    },
                    body,
                });
                if (res.status >= 200 && res.status < 300) {
                    let receiptId;
                    try {
                        const parsed = JSON.parse(res.bodyText);
                        receiptId = parsed.id ?? parsed.receipt_id;
                    }
                    catch {
                        // tolerate non-JSON success bodies
                    }
                    return { ok: true, status: res.status, attempts: attempt + 1, receiptId };
                }
                if (res.status >= 400 && res.status < 500) {
                    // 4xx is a client error -- don't retry
                    return {
                        ok: false,
                        status: res.status,
                        attempts: attempt + 1,
                        error: `client error: ${res.bodyText.slice(0, 256)}`,
                    };
                }
                // 5xx -- retry
                lastStatus = res.status;
                lastErr = `server status=${res.status}`;
            }
            catch (err) {
                lastErr = `transport error: ${err.message}`;
            }
            // Backoff before retry (unless we're out of attempts)
            if (attempt < this.options.maxRetries) {
                const backoff = this.options.baseBackoffMs * Math.pow(2, attempt);
                await this.options.sleep(backoff);
            }
        }
        return {
            ok: false,
            status: lastStatus,
            attempts: this.options.maxRetries + 1,
            error: lastErr ?? 'exhausted retries',
        };
    }
}
exports.ManifestPushEmitter = ManifestPushEmitter;
//# sourceMappingURL=manifest-push-emitter.js.map