/**
 * Editable SEO config — DB-backed overrides for the static `actuate.config.ts`
 * `seo` block + per-collection `CollectionSEOConfig`.
 *
 * Static config in `actuate.config.ts` is the source of truth at code level,
 * but admins need to be able to change site name, default OG image, robots
 * policy, default Schema.org types per collection, etc. without redeploying.
 *
 * Storage: a single Document row with `collection = '__seo_config__'` and
 * `data.{site, collections}`. The leading double underscore makes the slug
 * un-spellable as a normal collection (slugs are lowercased + dashed in the
 * config validator), so it can never collide with a user collection.
 *
 * Merge semantics: DB values take precedence over static config, field by
 * field. Removing a value in the UI (setting it to undefined / empty string)
 * falls back to the static default — so the admin panel is purely additive,
 * never destructive of code-level config.
 */
export const SEO_CONFIG_COLLECTION = '__seo_config__';
/** Safe defaults applied when the overrides document has no `module` block. */
export const DEFAULT_SEO_MODULE_SETTINGS = {
    sitemapEnabled: true,
    robotsEnabled: true,
    autoRegenerateSitemapOnPublish: true,
    pingSearchEnginesOnPublish: true,
    includeImagesInSitemap: true,
    includePostsInSitemap: true,
    indexEntireSite: true,
    noindexPaginatedArchives: true,
    noindexTagPages: false,
    robotsTxt: '',
    searchConsoleConnected: false,
    sitemapLastGeneratedAt: null,
};
/** Merge stored module settings over the defaults (field-by-field). */
export function resolveSeoModuleSettings(overrides) {
    const stored = overrides?.module ?? {};
    return {
        ...DEFAULT_SEO_MODULE_SETTINGS,
        ...Object.fromEntries(Object.entries(stored).filter(([, v]) => v !== undefined && v !== null)),
    };
}
/** Read the SEO overrides document. Returns null when no row exists yet. */
export async function getSeoOverrides(db) {
    const d = db;
    if (!d?.document?.findFirst)
        return null;
    try {
        const doc = await d.document.findFirst({
            where: { collection: SEO_CONFIG_COLLECTION, deletedAt: null },
            select: { data: true },
        });
        if (!doc)
            return null;
        const data = doc.data;
        if (!data || typeof data !== 'object')
            return null;
        return data;
    }
    catch {
        return null;
    }
}
/**
 * Upsert the SEO overrides document. The caller is responsible for auth/role
 * checks — this function only handles storage.
 */
export async function putSeoOverrides(db, overrides, userId) {
    const d = db;
    const data = {
        ...overrides,
        updatedAt: new Date().toISOString(),
        updatedBy: userId,
    };
    const existing = await d.document.findFirst({
        where: { collection: SEO_CONFIG_COLLECTION, deletedAt: null },
        select: { id: true },
    });
    if (existing) {
        await d.document.update({
            where: { id: existing.id },
            data: { data, updatedById: userId },
        });
    }
    else {
        await d.document.create({
            data: {
                collection: SEO_CONFIG_COLLECTION,
                data,
                status: 'PUBLISHED',
                title: 'SEO defaults',
                slug: 'seo-config',
                createdById: userId,
                updatedById: userId,
            },
        });
    }
    return data;
}
/**
 * Shallow-merge that drops undefined / empty-string values on the override
 * side so the UI can "unset" a field by submitting an empty string.
 */
function mergeFields(base, over) {
    const out = { ...(base ?? {}) };
    if (!over)
        return out;
    for (const [k, v] of Object.entries(over)) {
        if (v === undefined)
            continue;
        if (typeof v === 'string' && v.trim() === '')
            continue;
        if (v === null) {
            delete out[k];
            continue;
        }
        out[k] = v;
    }
    return out;
}
/**
 * Merge static site-wide SEO config with the DB overrides. Nested objects
 * (`robots`, `sitemap`, `ogImage`, `organization`) merge field-by-field;
 * primitive values use override-wins-if-set.
 */
export function mergeSiteSeoConfig(base, over) {
    if (!over)
        return { ...(base ?? {}) };
    // The SEO config interfaces are declared with explicit named fields and no
    // index signature, so we cast through Record<string, unknown> when calling
    // the generic merge helper. Behaviour is unchanged; this is purely a
    // structural-typing escape hatch.
    const merged = mergeFields(base, over);
    return {
        ...merged,
        robots: mergeFields(base?.robots, over.robots),
        sitemap: mergeFields(base?.sitemap, over.sitemap),
        ogImage: mergeFields(base?.ogImage, over.ogImage),
        organization: mergeFields(base?.organization, over.organization),
        verification: mergeFields(base?.verification, over.verification),
    };
}
/** Same merge semantics as the site-wide version, applied to a single collection. */
export function mergeCollectionSeoConfig(base, over) {
    if (!over)
        return { ...(base ?? {}) };
    const merged = mergeFields(base, over);
    return {
        ...merged,
        defaultRobots: mergeFields(base?.defaultRobots, over.defaultRobots),
        defaultOgImage: mergeFields(base?.defaultOgImage, over.defaultOgImage),
    };
}
/**
 * Return a new config object with DB-stored SEO overrides applied. The
 * static config is never mutated — callers receive a shallow copy with
 * `seo` and each collection's `seo` block replaced by the merged values.
 *
 * Routes that compose SEO output call this once per request, then thread
 * the result through to `composePageMeta` / sitemap helpers exactly like
 * the static config they replaced.
 */
export function applySeoOverrides(config, overrides) {
    if (!config)
        return null;
    if (!overrides)
        return config;
    const mergedSite = mergeSiteSeoConfig(config.seo, overrides.site);
    const mergedCollections = {};
    for (const [slug, col] of Object.entries(config.collections ?? {})) {
        const colOver = overrides.collections?.[slug];
        mergedCollections[slug] = colOver
            ? { ...col, seo: mergeCollectionSeoConfig(col.seo, colOver) }
            : col;
    }
    return {
        ...config,
        seo: mergedSite,
        collections: mergedCollections,
    };
}
//# sourceMappingURL=config-store.js.map