/**
 * Section content validation — shared by the admin editor's `validatePage`,
 * the REST `/page-sections/validate` endpoint, and the MCP
 * `validate_section_content` tool.
 *
 * Deterministic (no LLM, no I/O). Validates a SINGLE section's content
 * against its registry field schema. Page-level rules (title/path, "at
 * least one visible section", SEO warnings) live in the admin's
 * `validatePage` because they operate on the whole `EditorPage`.
 *
 * Messages are intentionally label-prefix-free (e.g. `"Heading" is
 * required.`) so callers can prepend their own context (`${sectionName}:
 * ...`) without double-labelling.
 */
import { getSectionType } from './registry.js';
const URL_OK = /^(https?:\/\/|\/|#|mailto:|tel:)/i;
function isBlank(v) {
    return v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
}
/** Label/URL field pairs: the URL must be valid when its label is filled in. */
const BUTTON_PAIRS = [
    ['primaryButtonLabel', 'primaryButtonUrl'],
    ['secondaryButtonLabel', 'secondaryButtonUrl'],
    ['buttonLabel', 'buttonUrl'],
];
/**
 * Validate one section's content + settings against its type schema.
 *
 * An unknown `sectionType` is a hard error (the agent asked for a type with
 * no renderer). Required fields, button URL validity, and image alt text
 * are enforced; everything else is advisory.
 */
export function validateSectionContent(sectionType, content, _settings) {
    const errors = [];
    const warnings = [];
    const def = getSectionType(sectionType);
    if (!def) {
        errors.push({
            sectionType,
            message: `Unknown section type "${sectionType}". It has no registered renderer.`,
        });
        return { valid: false, errors, warnings };
    }
    const data = content ?? {};
    for (const field of def.fields) {
        if (field.required && isBlank(data[field.key])) {
            errors.push({ sectionType, field: field.key, message: `"${field.label}" is required.` });
        }
    }
    for (const [labelKey, urlKey] of BUTTON_PAIRS) {
        const hasLabel = !isBlank(data[labelKey]);
        const url = data[urlKey];
        if (hasLabel && !isBlank(url) && typeof url === 'string' && !URL_OK.test(url.trim())) {
            errors.push({ sectionType, field: urlKey, message: `"${url}" is not a valid URL.` });
        }
        // A configured URL with no label is a soft issue — the button won't render.
        if (!hasLabel && !isBlank(data[urlKey])) {
            warnings.push({
                sectionType,
                field: labelKey,
                message: `A URL is set for "${urlKey}" but its button label is empty, so the button will not render.`,
            });
        }
    }
    // Image alt text required when an image is set (accessibility).
    if (!isBlank(data.imageUrl) && isBlank(data.imageAlt)) {
        errors.push({
            sectionType,
            field: 'imageAlt',
            message: 'image needs alt text for accessibility.',
        });
    }
    // Gallery images should each carry alt text (accessibility) — advisory so
    // it never blocks publish, but surfaced so it can be fixed.
    if (Array.isArray(data.images)) {
        const missingAlt = data.images.some((img) => {
            if (!img || typeof img !== 'object')
                return false;
            const row = img;
            return !isBlank(row.url) && isBlank(row.alt);
        });
        if (missingAlt) {
            warnings.push({
                sectionType,
                field: 'images',
                message: 'One or more gallery images are missing alt text for accessibility.',
            });
        }
    }
    return { valid: errors.length === 0, errors, warnings };
}
//# sourceMappingURL=validate.js.map