"use strict";
/**
 * Approval Notification Channels (W1.10 of F-NEW-PRODUCT-AGENT-VISIBILITY-CLUSTER-2026-05-12).
 *
 * Thomas directive 2026-05-12: "make sure in the UX/UI for the human in the
 * loop section we have the ability to email and/or send a text message to
 * the user for approval."
 *
 * Extends the existing approval-required pipeline (ApprovalRoute / ApprovalTicket
 * already shipped in packages/governance-server/src/approval-routing.ts) with
 * out-of-band notification fan-out: dashboard inbox (existing), email (new),
 * SMS (new). Per-reviewer channel preferences. Audit chain captures the
 * channel used.
 *
 * Transport adapters are pluggable so we can swap Resend ↔ SES ↔ SendGrid for
 * email and Twilio ↔ Vonage ↔ AWS-SNS for SMS without touching the dispatcher.
 *
 * @connexum/ai-governance  F-NEW-PRODUCT-APPROVAL-CHANNEL-EMAIL-SMS
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApprovalChannelDispatcher = void 0;
const NOOP_EMAIL = {
    async send() {
        return { ok: false, error: 'no email sender configured' };
    },
};
const NOOP_SMS = {
    async send() {
        return { ok: false, error: 'no SMS sender configured' };
    },
};
function renderEmailHtml(ctx, recipient) {
    return (`<p>Hi ${escapeHtml(recipient.name)},</p>` +
        `<p>An action by agent <strong>${escapeHtml(ctx.agentId)}</strong> in org <strong>${escapeHtml(ctx.orgId)}</strong> requires your approval.</p>` +
        `<p><strong>Tool:</strong> ${escapeHtml(ctx.toolName)}<br>` +
        `<strong>Risk:</strong> ${ctx.riskLevel.toUpperCase()}<br>` +
        `<strong>Summary:</strong> ${escapeHtml(ctx.summary)}<br>` +
        `<strong>SLA deadline:</strong> ${ctx.slaDeadline}</p>` +
        `<p><a href="${escapeAttr(ctx.approveUrl)}">Approve</a> · <a href="${escapeAttr(ctx.denyUrl)}">Deny</a></p>` +
        `<p>Ticket: <code>${escapeHtml(ctx.ticketId)}</code></p>`);
}
function renderEmailText(ctx, recipient) {
    return (`Hi ${recipient.name},\n\n` +
        `Agent ${ctx.agentId} (org ${ctx.orgId}) is requesting approval for tool ${ctx.toolName}.\n` +
        `Risk: ${ctx.riskLevel.toUpperCase()}\n` +
        `Summary: ${ctx.summary}\n` +
        `SLA deadline: ${ctx.slaDeadline}\n\n` +
        `Approve: ${ctx.approveUrl}\nDeny: ${ctx.denyUrl}\n\n` +
        `Ticket: ${ctx.ticketId}`);
}
function renderSmsBody(ctx) {
    // SMS bodies stay short. Caller can override by writing a custom dispatcher.
    return (`[my-cc] Approval needed: agent ${ctx.agentId} -> ${ctx.toolName} (${ctx.riskLevel.toUpperCase()}). ` +
        `Approve: ${ctx.approveUrl}`);
}
function escapeHtml(s) {
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function escapeAttr(s) {
    return s.replace(/"/g, '&quot;').replace(/</g, '&lt;');
}
/**
 * Dispatcher that fans out approval notifications across the requested
 * channels. Returns one NotificationOutcome per (recipient, channel) pair.
 */
class ApprovalChannelDispatcher {
    emailSender;
    smsSender;
    dashboardSink;
    constructor(options = {}) {
        this.emailSender = options.emailSender ?? NOOP_EMAIL;
        this.smsSender = options.smsSender ?? NOOP_SMS;
        this.dashboardSink = options.dashboardSink;
    }
    async dispatch(ctx, recipients) {
        const outcomes = [];
        for (const r of recipients) {
            const channels = r.channels && r.channels.length > 0 ? [...r.channels] : ['dashboard'];
            for (const ch of channels) {
                outcomes.push(await this.deliver(ctx, r, ch));
            }
        }
        return outcomes;
    }
    async deliver(ctx, recipient, channel) {
        if (channel === 'dashboard') {
            this.dashboardSink?.(ctx, recipient);
            return { recipientId: recipient.reviewerId, channel, delivered: true };
        }
        if (channel === 'email') {
            if (!recipient.email) {
                return {
                    recipientId: recipient.reviewerId,
                    channel,
                    delivered: false,
                    error: 'recipient has no email address configured',
                };
            }
            const res = await this.emailSender.send({
                to: recipient.email,
                subject: `[my-cc] Approval needed: ${ctx.toolName} (${ctx.riskLevel.toUpperCase()})`,
                html: renderEmailHtml(ctx, recipient),
                text: renderEmailText(ctx, recipient),
            });
            return {
                recipientId: recipient.reviewerId,
                channel,
                delivered: res.ok,
                providerMessageId: res.messageId,
                error: res.error,
            };
        }
        if (channel === 'sms') {
            if (!recipient.phoneE164) {
                return {
                    recipientId: recipient.reviewerId,
                    channel,
                    delivered: false,
                    error: 'recipient has no phone number configured',
                };
            }
            const res = await this.smsSender.send({
                to: recipient.phoneE164,
                body: renderSmsBody(ctx),
            });
            return {
                recipientId: recipient.reviewerId,
                channel,
                delivered: res.ok,
                providerMessageId: res.messageId,
                error: res.error,
            };
        }
        return {
            recipientId: recipient.reviewerId,
            channel,
            delivered: false,
            error: `unknown channel: ${channel}`,
        };
    }
}
exports.ApprovalChannelDispatcher = ApprovalChannelDispatcher;
//# sourceMappingURL=approval-notifications.js.map