/**
 * @sanity/ailf-core — Artifact-persistence degradation detector
 * (`W-detect-wholesale-artifact-loss`).
 *
 * Pure, I/O-free check that runs at report-assembly time. It reads a run's
 * `ArtifactManifest` and decides whether the run suffered *wholesale* artifact
 * loss: a remote artifact backend (GCS / API Gateway) was configured, yet zero
 * (or below a configured threshold) of the produced artifact sections actually
 * persisted there — every section fell back to `store: "local"`.
 *
 * ## Why a manifest-only check is insufficient
 *
 * Artifact uploads are non-blocking by design (P5): each failure warns and the
 * fanout falls through to the local writer, downgrading the ref to
 * `store: "local"`. When *all* uploads fail (e.g. the Node 22.23.0 /
 * node-fetch@2 OAuth-token regression that failed 95/95 uploads of
 * `run_20260625T084753Z_0fk47ex2`), every manifest section is `store: "local"`
 * — but a legitimate local-only run (no remote credentials) produces an
 * *identical* manifest. The `store` labels alone cannot tell the two apart, so
 * the caller must supply `remoteConfigured` (derived from the actual writer
 * wiring). This detector only fires when remote was configured.
 *
 * ## Relationship to the existing degraded guard
 *
 * The enrichment guard (`detectReportDegradation`) fires on missing inline
 * `summary.testResults`. This one is orthogonal: in the motivating incident
 * the inline `testResults` were present (8 entries) — what was missing was the
 * *GCS persistence* of `testOutputs` / `rawResults`. Both feed the same D0059
 * `ReportDegradation` mechanism via a distinct `reason`.
 *
 * @see docs/decisions/D0059-report-validity-axis-and-trustworthiness-gate.md
 * @see docs/decisions/D0032-run-anchored-artifact-store.md
 */
/**
 * Default persisted-fraction threshold: flag only when the fraction of produced
 * sections that reached the remote store is `<= 0` — i.e. literally zero
 * persisted (wholesale loss). Partial loss (some sections local, some remote)
 * is left to the writer's per-entry "local fallback" warning rather than
 * stamping the whole report degraded, which keeps false positives at zero for
 * the common case of a single transient blip on one section.
 */
export const DEFAULT_WHOLESALE_LOSS_THRESHOLD = 0;
/**
 * Inspect a run manifest for wholesale artifact-persistence failure.
 *
 * Returns a `ReportArtifactPersistenceDegradation` marker when a remote backend
 * was configured but the fraction of produced sections that reached it is at or
 * below `threshold`; otherwise `undefined` (a healthy or legitimately
 * local-only run). Pure: same input → same output.
 *
 * The denominator is the set of sections actually present in the manifest (the
 * ones this run produced); the numerator counts those whose canonical
 * `store` is `"gcs"`. A run that produced no artifact sections is never flagged
 * (nothing was lost).
 */
export function detectArtifactPersistenceDegradation(manifest, options) {
    if (!options.remoteConfigured)
        return undefined;
    if (!manifest)
        return undefined;
    const threshold = options.threshold ?? DEFAULT_WHOLESALE_LOSS_THRESHOLD;
    const produced = [];
    const notPersisted = [];
    let persistedCount = 0;
    for (const [type, ref] of Object.entries(manifest)) {
        if (!ref)
            continue;
        produced.push(type);
        if (ref.store === "gcs") {
            persistedCount++;
        }
        else {
            notPersisted.push(type);
        }
    }
    // Nothing was produced — there is no loss to report.
    if (produced.length === 0)
        return undefined;
    const persistedFraction = persistedCount / produced.length;
    if (persistedFraction > threshold)
        return undefined;
    const detail = persistedCount === 0
        ? `A remote artifact store was configured but none of the ${produced.length} ` +
            `produced artifact section(s) were persisted to it — every section fell ` +
            `back to local-only storage (wholesale upload failure). The report's ` +
            `scores are intact, but its externalized drill-downs (per-test outputs, ` +
            `raw results, etc.) do not exist remotely and cannot be recovered or ` +
            `re-interpreted.`
        : `A remote artifact store was configured but only ${persistedCount} of ` +
            `${produced.length} produced artifact section(s) were persisted to it ` +
            `(below the ${threshold} threshold); the remaining sections are ` +
            `local-only and unavailable to remote readers.`;
    return {
        reason: "artifacts-not-persisted",
        missing: notPersisted,
        detail,
    };
}
