/**
 * Typed authoring helper for custom section types.
 *
 * `defineSectionTypes` is to `sections.types` what `defineConfig` is to the
 * config file: an identity function that gives consumers full type inference
 * and eager, fail-fast validation at module-evaluation time.
 *
 * The runtime registry (`registerSectionTypes`) deliberately SKIPS invalid
 * entries so a malformed config can never crash a running site — but that
 * silence makes authoring mistakes (a typo'd field type, an id that collides
 * with a built-in) surface only as a mysteriously missing section type in the
 * Add Section gallery. This helper is the loud counterpart: it throws a
 * single aggregated error naming every problem with its exact path, so
 * mistakes are caught where the types are written.
 *
 * Usage in a consumer app:
 *
 * ```ts
 * import { defineConfig, defineSectionTypes } from '@actuate-media/cms-core'
 *
 * const customSections = defineSectionTypes([
 *   {
 *     id: 'pricing',
 *     name: 'Pricing Table',
 *     scope: ['page'],
 *     fields: [{ key: 'heading', label: 'Heading', type: 'text', required: true }],
 *   },
 * ])
 *
 * export default defineConfig({ sections: { types: customSections }, ... })
 * ```
 */
import { SECTION_TYPES } from './registry.js';
/** Runtime mirror of the {@link SectionFieldType} union, for validation. */
export const SECTION_FIELD_TYPES = [
    'text',
    'textarea',
    'richText',
    'url',
    'media',
    'select',
    'boolean',
    'color',
    'relationship',
    'repeater',
    'stats',
    'gallery',
];
const FIELD_TYPE_SET = new Set(SECTION_FIELD_TYPES);
const SCOPES = new Set(['page', 'post']);
function isNonBlankString(v) {
    return typeof v === 'string' && v.trim().length > 0;
}
function validateFields(fields, path, errors) {
    if (!Array.isArray(fields)) {
        errors.push(`${path}.fields must be an array (use [] for a type with no editable fields)`);
        return;
    }
    const seenKeys = new Set();
    fields.forEach((field, i) => {
        const fpath = `${path}.fields[${i}]`;
        if (!field || typeof field !== 'object') {
            errors.push(`${fpath} must be an object`);
            return;
        }
        if (!isNonBlankString(field.key)) {
            errors.push(`${fpath}.key must be a non-empty string`);
        }
        else if (seenKeys.has(field.key)) {
            errors.push(`${fpath}.key "${field.key}" is duplicated within ${path}`);
        }
        else {
            seenKeys.add(field.key);
        }
        if (!isNonBlankString(field.label)) {
            errors.push(`${fpath}.label must be a non-empty string`);
        }
        if (!FIELD_TYPE_SET.has(field.type)) {
            errors.push(`${fpath}.type "${String(field.type)}" is not a known field type. ` +
                `Valid types: ${SECTION_FIELD_TYPES.join(', ')}`);
        }
        if (field.type === 'select' && (!Array.isArray(field.options) || field.options.length === 0)) {
            errors.push(`${fpath} is a select field and must declare a non-empty \`options\` array`);
        }
        if (field.type === 'repeater') {
            if (!Array.isArray(field.fields) || field.fields.length === 0) {
                errors.push(`${fpath} is a repeater and must declare a non-empty \`fields\` sub-schema`);
            }
            else {
                validateFields(field.fields, fpath, errors);
            }
        }
    });
}
/**
 * Validate an array of custom section type definitions, returning every
 * problem found (empty array = valid). Exposed for tooling that wants the
 * issue list without throwing; most consumers should use
 * {@link defineSectionTypes} instead.
 */
export function validateSectionTypeInputs(types) {
    const errors = [];
    if (!Array.isArray(types)) {
        return ['defineSectionTypes expects an array of section type definitions'];
    }
    const seenIds = new Set();
    types.forEach((type, i) => {
        const path = `types[${i}]`;
        if (!type || typeof type !== 'object') {
            errors.push(`${path} must be an object`);
            return;
        }
        if (!isNonBlankString(type.id)) {
            errors.push(`${path}.id must be a non-empty string`);
        }
        else {
            if (type.id in SECTION_TYPES) {
                errors.push(`${path}.id "${type.id}" collides with a built-in section type. ` +
                    'Built-ins always win on an id collision, so this type would be silently ignored — pick a different id.');
            }
            if (seenIds.has(type.id)) {
                errors.push(`${path}.id "${type.id}" is duplicated — section type ids must be unique`);
            }
            seenIds.add(type.id);
        }
        if (!isNonBlankString(type.name)) {
            errors.push(`${path}.name must be a non-empty string`);
        }
        if (!Array.isArray(type.scope) || type.scope.length === 0) {
            errors.push(`${path}.scope must be a non-empty array of 'page' | 'post'`);
        }
        else {
            for (const s of type.scope) {
                if (!SCOPES.has(s)) {
                    errors.push(`${path}.scope contains "${String(s)}" — valid scopes are 'page' and 'post'`);
                }
            }
        }
        validateFields(type.fields, path, errors);
    });
    return errors;
}
/**
 * Define custom section types with eager validation. Returns the input
 * unchanged (identity function) so the result can be passed straight to
 * `sections.types` in `actuate.config.ts` — and to
 * `registerSectionRenderer` lookups on the public site.
 *
 * @throws Error listing every validation problem when any definition is
 *   malformed, duplicates an id, or shadows a built-in section type.
 */
export function defineSectionTypes(types) {
    const errors = validateSectionTypeInputs(types);
    if (errors.length > 0) {
        throw new Error(`Invalid custom section type definition${errors.length > 1 ? 's' : ''}:\n` +
            errors.map((e) => `  - ${e}`).join('\n'));
    }
    return types;
}
//# sourceMappingURL=define.js.map