/**
 * sanity/perspective-pin-guard.ts
 *
 * Stale-pin guard for task-level perspective canonical doc refs.
 *
 * A task's `PerspectiveDocRef` pins it to a content release, and that
 * expansion is unconditional — every run that includes the task fetches
 * whatever the release currently contains, with no signal when the pin
 * has gone stale (release published/archived/deleted) or was wrong from
 * the start (release unrelated to the task's area). The audited
 * nuxt-integration case silently injected three Canvas articles into a
 * frameworks task's context on every run.
 *
 * This module computes operator-visible warnings for both cases. It is
 * deliberately **non-fatal**: expansion stays unconditional, the guard
 * only warns, so a stale pin is surfaced without breaking runs.
 *
 * "Unrelated to the task's area" reuses the feature-registry section→area
 * mapping that release classification already uses
 * (`buildSectionMapping`), rather than any hardcoded section list. The
 * guard stays quiet whenever the signal is inconclusive — no feature
 * registry available, the task has no area, the area is unknown to the
 * registry, or none of the expanded articles carry a resolvable section.
 *
 * @see docs/audits/2026-06-10-nuxt-stale-perspective-pin.md
 * @see docs/design-docs/canonical-doc-resolution.md
 */
import { buildSectionMapping } from "../pipeline/release-classification.js";
// ---------------------------------------------------------------------------
// Warning computation (pure — no Sanity access)
// ---------------------------------------------------------------------------
/**
 * Compute stale-pin warnings for one perspective ref expansion.
 *
 * Two non-fatal conditions are flagged:
 *
 * 1. **Empty expansion** — the pinned release expanded to zero articles.
 *    The release no longer exists, was published/archived, or contains no
 *    article documents; in every case the pin no longer does what its
 *    author intended.
 * 2. **Unrelated expansion** — the release expanded to articles, but none
 *    of their sections relate to the pinning task's area per the feature
 *    registry's section→area mapping. Warned per task (different tasks
 *    pinning the same release can have different areas). A release whose
 *    articles span several sections counts as related if *any* expanded
 *    section maps to the task's area — multi-section releases are
 *    legitimate, so only a complete mismatch is flagged.
 *
 * Returns human-readable warning lines; empty array when nothing is
 * suspect or the relatedness signal is inconclusive.
 */
export function buildPerspectivePinWarnings(perspectiveId, taskRefs, articles, features) {
    // A task can carry several refs pinning the same release (the audited
    // task carried two) — warn once per task, not once per ref.
    const uniqueTaskRefs = [
        ...new Map(taskRefs.map((r) => [r.taskId, r])).values(),
    ];
    if (articles.length === 0) {
        const taskIds = uniqueTaskRefs.map((r) => r.taskId);
        return [
            `Stale perspective pin: release "${perspectiveId}" expanded to no articles ` +
                `(pinned by task(s): ${taskIds.join(", ")}). The release may have been ` +
                `published, archived, or deleted — update or remove the task's ` +
                `perspective contextDocs entry.`,
        ];
    }
    if (!features || features.length === 0)
        return [];
    const sectionToAreas = buildSectionMapping(features);
    const knownAreas = new Set([...sectionToAreas.values()].flat());
    const expandedSections = [
        ...new Set(articles.map((a) => a.sectionSlug).filter((s) => Boolean(s))),
    ];
    // No resolvable sections — the relatedness signal is inconclusive.
    if (expandedSections.length === 0)
        return [];
    const warnings = [];
    for (const ref of uniqueTaskRefs) {
        // Without an area (or with an area the registry doesn't know),
        // relatedness cannot be assessed — stay quiet rather than guess.
        const area = ref.area;
        if (!area || !knownAreas.has(area))
            continue;
        const related = expandedSections.some((section) => (sectionToAreas.get(section) ?? []).includes(area));
        if (related)
            continue;
        warnings.push(`Suspect perspective pin: release "${perspectiveId}" expands to ` +
            `${articles.length} article(s) in section(s) [${expandedSections.join(", ")}], ` +
            `none related to area "${area}" of task "${ref.taskId}". The pin ` +
            `may be stale or wrong — verify the task's perspective contextDocs entry.`);
    }
    return warnings;
}
