import { getActuateConfig } from '../config/runtime.js';
import { getSectionType } from '../sections/registry.js';
import { sanitizeHtml } from './sanitize.js';
/** Bound recursion against pathological / adversarial nesting depth. */
const MAX_DEPTH = 10;
function isPlainObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
/** Block-data keys that conventionally carry author HTML in the page-builder tree. */
const HTML_BLOCK_KEYS = ['content', 'html'];
/** Resolve a `blocks` field item to its block definition via the conventional discriminator. */
function pickBlockSlug(item) {
    for (const key of ['blockType', 'blockName', 'blockSlug', '_type']) {
        const v = item[key];
        if (typeof v === 'string')
            return v;
    }
    return undefined;
}
function sanitizeObjectByFields(obj, fields, depth) {
    const out = { ...obj };
    for (const [key, def] of Object.entries(fields)) {
        if (key in out)
            out[key] = sanitizeByFieldDef(out[key], def, depth);
    }
    return out;
}
function sanitizeByFieldDef(value, def, depth) {
    if (depth > MAX_DEPTH)
        return value;
    switch (def.type) {
        case 'richText':
            return typeof value === 'string' ? sanitizeHtml(value) : value;
        case 'array': {
            if (!Array.isArray(value))
                return value;
            const arrayDef = def;
            return value.map((item) => isPlainObject(item) ? sanitizeObjectByFields(item, arrayDef.fields, depth + 1) : item);
        }
        case 'blocks': {
            if (!Array.isArray(value))
                return value;
            const blocksDef = def;
            return value.map((item) => {
                if (!isPlainObject(item))
                    return item;
                const slug = pickBlockSlug(item);
                const blockDef = slug ? blocksDef.blocks.find((b) => b.slug === slug) : undefined;
                if (!blockDef)
                    return item;
                return sanitizeObjectByFields(item, blockDef.fields, depth + 1);
            });
        }
        default:
            return value;
    }
}
/** Sanitize the rich-text slots of a single canonical `PageSection`. */
function sanitizeSection(section) {
    if (!isPlainObject(section))
        return section;
    const typeId = typeof section.sectionType === 'string' ? section.sectionType : undefined;
    const def = typeId ? getSectionType(typeId) : undefined;
    if (!def || !isPlainObject(section.content))
        return section;
    let mutated = false;
    const content = { ...section.content };
    for (const field of def.fields) {
        if (field.type === 'richText' && typeof content[field.key] === 'string') {
            content[field.key] = sanitizeHtml(content[field.key]);
            mutated = true;
        }
    }
    return mutated ? { ...section, content } : section;
}
/**
 * Sanitize a page-builder layout node tree in place-immutably, cleaning the
 * rich-text block bodies. Containers/rows/columns recurse through `children`.
 */
export function sanitizeLayoutTree(layout) {
    return sanitizeNode(layout, 0);
}
function sanitizeNode(node, depth) {
    if (depth > MAX_DEPTH || !isPlainObject(node))
        return node;
    const out = { ...node };
    if (out.type === 'block' && isPlainObject(out.data)) {
        const data = { ...out.data };
        for (const key of HTML_BLOCK_KEYS) {
            if (typeof data[key] === 'string')
                data[key] = sanitizeHtml(data[key]);
        }
        out.data = data;
    }
    if (Array.isArray(out.children)) {
        out.children = out.children.map((child) => sanitizeNode(child, depth + 1));
    }
    return out;
}
/**
 * Sanitize every author-HTML sink reachable in a document's `data` payload for
 * the given collection: top-level + nested rich-text fields and the canonical
 * `sections[]` rich-text slots. The page-builder `_layout` tree is sanitized
 * separately via {@link sanitizeLayoutTree} because it is split into an envelope
 * before this runs.
 */
export function sanitizeDocumentHtml(data, collection) {
    const fields = getActuateConfig()?.collections?.[collection]?.fields ?? {};
    let result = sanitizeObjectByFields(data, fields, 0);
    // `sections` is the canonical page-content array; it is NOT part of the
    // collection field config, so resolve its HTML slots from the registry.
    if (Array.isArray(result.sections)) {
        result = { ...result, sections: result.sections.map(sanitizeSection) };
    }
    return result;
}
//# sourceMappingURL=sanitize-document.js.map