/**
 * Auto-301 planning for slug/path changes.
 *
 * When a *published* document's resolved URL changes, the old URL was already
 * crawled/linked/indexed and must not 404. This module computes — as a pure,
 * fully testable function — the redirect mutations needed to keep the old URL
 * alive without building chains or loops:
 *
 *   1. Drop any rule whose source is the new (now-live) path, which would
 *      otherwise redirect the page away from itself.
 *   2. Repoint any rule that pointed at the old path so it points at the new
 *      path directly (chain flattening — never old→mid→new).
 *   3. Add old→new, but only if the simulated post-mutation rule set stays
 *      loop-free (e.g. a pre-existing new→old rule we are not removing).
 *
 * The caller (actions.ts) applies the returned plan in a single transaction.
 */
import { normalizeRedirectPath, wouldCreateLoop } from './graph.js';
const EMPTY_PLAN = { upsert: null, deleteIds: [], repoint: [] };
/** True when the plan would make no database changes. */
export function isEmptyPlan(plan) {
    return !plan.upsert && plan.deleteIds.length === 0 && plan.repoint.length === 0;
}
/**
 * Compute the redirect mutations for a slug/path change. Both paths are
 * site-relative (e.g. `/old-about`); normalization (leading slash, no trailing
 * slash, lowercase) is applied internally and matches request-time matching.
 */
export function planSlugChangeRedirects(params) {
    const oldN = normalizeRedirectPath(params.oldPath);
    const newN = normalizeRedirectPath(params.newPath);
    // Nothing to do when the URL did not actually change. Never redirect the
    // site root onto itself or onto a child — a homepage "move" is almost always
    // an unrelated path edit and a `/` → /x rule would break the whole site.
    if (!oldN || !newN || oldN === newN || oldN === '/')
        return EMPTY_PLAN;
    const deleteIds = params.existing
        .filter((r) => normalizeRedirectPath(r.from) === newN)
        .map((r) => r.id);
    const repoint = params.existing
        .filter((r) => !deleteIds.includes(r.id) &&
        normalizeRedirectPath(r.to) === oldN &&
        normalizeRedirectPath(r.from) !== newN)
        .map((r) => ({ id: r.id, destination: newN }));
    // Simulate the post-mutation rule set (deletes applied, repoints applied)
    // and only add the new edge if it stays loop-free.
    const remaining = params.existing
        .filter((r) => !deleteIds.includes(r.id))
        .map((r) => (repoint.some((p) => p.id === r.id) ? { ...r, to: newN } : r));
    if (wouldCreateLoop(remaining, oldN, newN)) {
        return { upsert: null, deleteIds, repoint };
    }
    return { upsert: { source: oldN, destination: newN }, deleteIds, repoint };
}
//# sourceMappingURL=auto.js.map