/**
 * Form submission notifications — admin alerts + submitter autoresponder.
 *
 * Email is sent through the configured `platform.email` adapter (the same path
 * password-reset uses). When no adapter is configured we no-op so a missing
 * email integration never breaks the submission. All sends are best-effort and
 * never throw into the caller — the submission is already persisted by the time
 * we run.
 *
 * Templates support `{{placeholder}}` interpolation over the submission's field
 * values plus `{{formName}}` / `{{entryNumber}}`. Field values are HTML-escaped
 * before interpolation, so a value can never inject markup into the email.
 */
import { getActuateConfig } from '../config/runtime.js';
import { resolveEmailSenderAsync } from '../email/resolve.js';
import { createLogger } from '../diagnostics/logger.js';
const logger = createLogger('forms-notify');
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function escapeHtml(value) {
    return value
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
}
function valueToString(value) {
    if (value == null)
        return '';
    if (Array.isArray(value))
        return value.map((v) => String(v)).join(', ');
    if (typeof value === 'boolean')
        return value ? 'Yes' : 'No';
    return String(value);
}
/** A field is sensitive when explicitly encrypted or classified high-risk PII. */
function isSensitive(field) {
    return Boolean(field.encrypted) || field.piiClassification === 'high';
}
/** Interpolate `{{key}}` tokens. Values are HTML-escaped to prevent injection. */
function renderTemplate(template, vars) {
    return template.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_, key) => Object.prototype.hasOwnProperty.call(vars, key) ? escapeHtml(vars[key] ?? '') : '');
}
function buildVars(ctx) {
    const vars = {
        formName: ctx.formName,
        entryNumber: ctx.entryNumber != null ? `#${String(ctx.entryNumber).padStart(5, '0')}` : '',
        senderName: ctx.senderName ?? '',
        senderEmail: ctx.senderEmail ?? '',
    };
    for (const field of ctx.fields) {
        if (field.type === 'honeypot')
            continue;
        vars[field.key] = valueToString(ctx.data[field.key]);
    }
    return vars;
}
function buildFieldsTable(ctx, excludeSensitive) {
    const rows = [];
    const lines = [];
    for (const field of ctx.fields) {
        if (field.type === 'honeypot' || field.type === 'divider' || field.type === 'richtext')
            continue;
        const label = escapeHtml(field.label || field.key);
        const sensitive = excludeSensitive && isSensitive(field);
        const raw = sensitive ? '[hidden]' : valueToString(ctx.data[field.key]);
        const value = escapeHtml(raw) || '<span style="color:#999">—</span>';
        rows.push(`<tr><td style="padding:6px 12px;font-weight:500;color:#111;border-bottom:1px solid #eee;vertical-align:top">${label}</td>` +
            `<td style="padding:6px 12px;color:#333;border-bottom:1px solid #eee">${value}</td></tr>`);
        lines.push(`${field.label || field.key}: ${sensitive ? '[hidden]' : valueToString(ctx.data[field.key])}`);
    }
    const html = `<table style="border-collapse:collapse;width:100%;font-family:system-ui,sans-serif;font-size:14px">${rows.join('')}</table>`;
    return { html, text: lines.join('\n') };
}
function adminEntryUrl(formId) {
    const config = getActuateConfig();
    const siteUrl = config?.seo?.siteUrl;
    if (!siteUrl)
        return null;
    const adminPath = config?.admin?.path ?? '/admin';
    return `${siteUrl.replace(/\/$/, '')}${adminPath}/forms/${formId}/submissions`;
}
/** Build the admin notification email (subject/html/text) for a submission. */
export function buildNotificationEmail(settings, ctx) {
    const vars = buildVars(ctx);
    const subject = settings.subjectTemplate
        ? renderTemplate(settings.subjectTemplate, vars)
        : `New submission: ${ctx.formName}`;
    const includeFields = settings.includeFields !== false;
    const table = includeFields
        ? buildFieldsTable(ctx, Boolean(settings.excludeSensitiveFields))
        : { html: '', text: '' };
    const intro = settings.messageTemplate
        ? renderTemplate(settings.messageTemplate, vars)
        : `You received a new submission${ctx.entryNumber != null ? ` (${vars.entryNumber})` : ''} on <strong>${escapeHtml(ctx.formName)}</strong>.`;
    const link = adminEntryUrl(ctx.formId);
    const linkHtml = link
        ? `<p style="margin-top:16px"><a href="${escapeHtml(link)}" style="color:#2563eb">View in admin →</a></p>`
        : '';
    const html = `<div style="font-family:system-ui,sans-serif;color:#111;max-width:640px">
<p style="font-size:15px">${intro}</p>
${table.html}
${linkHtml}
</div>`;
    const textParts = [stripTags(intro), table.text, link ? `View in admin: ${link}` : ''].filter(Boolean);
    return { subject, html, text: textParts.join('\n\n') };
}
function stripTags(html) {
    return html
        .replace(/<[^>]+>/g, '')
        .replace(/&nbsp;/g, ' ')
        .trim();
}
/** Resolve the reply-to address from the configured field, if it's a valid email. */
function resolveReplyTo(settings, ctx) {
    if (settings.replyToFieldKey) {
        const v = valueToString(ctx.data[settings.replyToFieldKey]).trim();
        if (EMAIL_RE.test(v))
            return v;
    }
    if (ctx.senderEmail && EMAIL_RE.test(ctx.senderEmail))
        return ctx.senderEmail;
    return undefined;
}
/** Build the submitter autoresponder email. */
export function buildAutoresponderEmail(settings, ctx) {
    const vars = buildVars(ctx);
    const subject = settings.autoresponderSubject
        ? renderTemplate(settings.autoresponderSubject, vars)
        : `We received your submission`;
    const body = settings.autoresponderBody
        ? renderTemplate(settings.autoresponderBody, vars)
        : `Thanks${ctx.senderName ? `, ${escapeHtml(ctx.senderName)}` : ''}! We received your submission and will be in touch soon.`;
    const html = `<div style="font-family:system-ui,sans-serif;color:#111;max-width:640px"><p style="font-size:15px">${body}</p></div>`;
    return { subject, html, text: stripTags(body) };
}
async function sendEmail(options) {
    const email = await resolveEmailSenderAsync();
    if (!email)
        return false;
    await email.send(options);
    return true;
}
/**
 * Send admin notifications + autoresponder for a submission. Best-effort: logs
 * and swallows per-recipient failures so one bad address can't block the rest
 * or surface to the public submitter.
 */
export async function sendFormNotifications(settings, ctx) {
    const result = { notified: 0, autoresponded: false };
    if (!(await resolveEmailSenderAsync())) {
        return { ...result, skippedReason: 'no-email-adapter' };
    }
    if (!settings?.enabled) {
        return { ...result, skippedReason: 'notifications-disabled' };
    }
    const recipients = (settings.recipients ?? []).filter((r) => EMAIL_RE.test(r.trim()));
    if (recipients.length > 0) {
        const email = buildNotificationEmail(settings, ctx);
        const replyTo = resolveReplyTo(settings, ctx);
        for (const to of recipients) {
            try {
                await sendEmail({
                    to: to.trim(),
                    subject: email.subject,
                    html: email.html,
                    text: email.text,
                    replyTo,
                });
                result.notified++;
            }
            catch (err) {
                logger.error('notification send failed', {
                    formId: ctx.formId,
                    reason: err instanceof Error ? err.message : String(err),
                });
            }
        }
    }
    if (settings.autoresponderEnabled) {
        const key = settings.autoresponderEmailFieldKey;
        const target = key ? valueToString(ctx.data[key]).trim() : (ctx.senderEmail ?? '');
        if (target && EMAIL_RE.test(target)) {
            try {
                const auto = buildAutoresponderEmail(settings, ctx);
                await sendEmail({ to: target, subject: auto.subject, html: auto.html, text: auto.text });
                result.autoresponded = true;
            }
            catch (err) {
                logger.error('autoresponder send failed', {
                    formId: ctx.formId,
                    reason: err instanceof Error ? err.message : String(err),
                });
            }
        }
    }
    return result;
}
//# sourceMappingURL=notify.js.map