/**
 * Field-level encryption for form submission values (PII at rest).
 *
 * A field is encrypted when it is explicitly flagged `encrypted`, or when the
 * site enables `forms.encryptSensitiveFields` and the field is classified as
 * high-risk PII. Values are encrypted with the same `enc:v1:` envelope used for
 * other secrets (`secret-storage`), so each stored value is self-describing —
 * the read path decrypts anything carrying the prefix and passes plaintext
 * through untouched. This keeps legacy/seed rows and key-less dev environments
 * working without a migration.
 *
 * Note: the operational `senderName` / `senderEmail` / `senderPhone` columns and
 * the `preview` snippet are intentionally derived BEFORE encryption and stored
 * in plaintext so the submissions inbox can list/search them. Encryption here
 * protects the full submission `data` / `normalizedData` blobs at rest.
 */
import { encryptSecret, decryptSecret, isEncrypted } from '../security/secret-storage.js';
import { getActuateConfig } from '../config/runtime.js';
/** Whether a field's stored value should be encrypted at rest. */
export function fieldShouldEncrypt(field) {
    if (field.encrypted)
        return true;
    const forms = getActuateConfig()?.forms;
    return Boolean(forms?.encryptSensitiveFields) && field.piiClassification === 'high';
}
async function encryptValue(value) {
    if (typeof value === 'string' && value.length > 0)
        return encryptSecret(value);
    if (Array.isArray(value)) {
        return Promise.all(value.map((v) => (typeof v === 'string' && v.length > 0 ? encryptSecret(v) : v)));
    }
    return value;
}
async function decryptValue(value) {
    if (typeof value === 'string' && isEncrypted(value))
        return decryptSecret(value);
    if (Array.isArray(value)) {
        return Promise.all(value.map((v) => (typeof v === 'string' && isEncrypted(v) ? decryptSecret(v) : v)));
    }
    return value;
}
/**
 * Encrypt the sensitive values in a submission data record. Returns a new
 * object; non-sensitive keys are copied through unchanged. Also returns the set
 * of keys that were encrypted (useful for masking decisions / auditing).
 */
export async function encryptSubmissionData(fields, data) {
    const sensitive = new Set(fields.filter(fieldShouldEncrypt).map((f) => f.key));
    if (sensitive.size === 0)
        return { data, encryptedKeys: [] };
    const out = { ...data };
    const encryptedKeys = [];
    for (const key of sensitive) {
        if (!(key in out))
            continue;
        out[key] = await encryptValue(out[key]);
        encryptedKeys.push(key);
    }
    return { data: out, encryptedKeys };
}
/**
 * Decrypt any encrypted values in a stored submission data record. Operates on
 * the `enc:v1:` prefix, so it is safe to call on records that were never
 * encrypted (plaintext passes through). Throws only if a value is encrypted but
 * `CMS_ENCRYPTION_KEY` is missing — fail-closed, never silently leak ciphertext.
 */
export async function decryptSubmissionData(data) {
    if (!data)
        return null;
    let touched = false;
    const out = {};
    for (const [key, value] of Object.entries(data)) {
        const next = await decryptValue(value);
        if (next !== value)
            touched = true;
        out[key] = next;
    }
    return touched ? out : data;
}
//# sourceMappingURL=encryption.js.map