#!/bin/sh
# Renders every Kustomize overlay in the repo and compares the result against a
# recorded baseline. The overlay set is discovered, never hardcoded. This is the regression gate for refactors that move
# behaviour between control formats: a change that claims to preserve behaviour
# has to produce a byte-identical render, and this proves it in seconds.
#
#   tools/render-baseline.sh check     # render everything, diff against baseline
#   tools/render-baseline.sh update    # re-record the baseline after an intended change
#   tools/render-baseline.sh list      # list the overlays that will be rendered
#
# Baselines are digests, not full manifests — 178 overlays render to well over a
# megabyte of YAML, and storing that would make every intended change an
# unreadable diff. A digest answers the only question the gate asks: did this
# render change? When one does change, `check` names the overlay and prints the
# unified diff for that overlay alone.
#
# `kubectl kustomize` output is deterministic, so an unchanged overlay always
# reproduces its digest. Overlays that fail to render are recorded as such
# rather than skipped, so a refactor cannot quietly break a render and pass.
#
# Requires kubectl with built-in Kustomize support. Reads nothing from AWS and
# contacts no cluster; unresolved placeholder tokens are expected in the output
# and are part of what the baseline pins.
set -eu

repo_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
baseline_file="$repo_root/tools/render-baseline.txt"
mode=${1:-check}

overlays() {
    find "$repo_root/personas" -name kustomization.yaml -print \
        | sed "s|^$repo_root/||; s|/kustomization.yaml$||" \
        | LC_ALL=C sort
}

# Digest one overlay.
#
# Render first, then explain a failure -- classifying by grepping for keys would
# label a genuinely broken overlay (one that lost its `resources:`) as fine by
# design. Two kinds legitimately do not render alone: a Component, which exists to
# be included by another kustomization, and a kustomization with no content beyond
# apiVersion/kind, used as an anchor for a parent. Anything else that fails is
# recorded as FAILED-TO-RENDER so that "it stopped rendering" surfaces as a
# baseline change rather than vanishing from the report.
digest_overlay() {
    overlay=$1
    if output=$(cd "$repo_root" && kubectl kustomize "$overlay" 2>/dev/null); then
        printf '%s' "$output" | digest
        return
    fi
    kustomization="$repo_root/$overlay/kustomization.yaml"
    if grep -q '^kind: Component' "$kustomization" 2>/dev/null; then
        printf 'NOT-STANDALONE-component'
    elif [ "$(grep -cvE '^(apiVersion|kind):|^ *$|^ *#' "$kustomization" 2>/dev/null)" = "0" ]; then
        printf 'NOT-STANDALONE-no-resources'
    else
        printf 'FAILED-TO-RENDER'
    fi
}

# sha256sum on most Linux images, shasum on macOS.
digest() {
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum | cut -d' ' -f1
    else
        shasum -a 256 | cut -d' ' -f1
    fi
}

record() {
    overlays | while IFS= read -r overlay; do
        printf '%s  %s\n' "$(digest_overlay "$overlay")" "$overlay"
    done
}

case "$mode" in
list)
    overlays
    ;;
update)
    printf 'rendering %s overlays ...\n' "$(overlays | wc -l | tr -d ' ')" >&2
    record >"$baseline_file"
    printf 'wrote %s\n' "${baseline_file#"$repo_root"/}" >&2
    ;;
check)
    if [ ! -f "$baseline_file" ]; then
        echo "no baseline recorded; run: tools/render-baseline.sh update" >&2
        exit 1
    fi
    current=$(mktemp)
    trap 'rm -f "$current"' EXIT
    printf 'rendering %s overlays ...\n' "$(overlays | wc -l | tr -d ' ')" >&2
    record >"$current"

    if diff -q "$baseline_file" "$current" >/dev/null 2>&1; then
        printf 'render unchanged across %s overlays\n' "$(wc -l <"$current" | tr -d ' ')" >&2
        exit 0
    fi

    echo >&2
    echo "RENDER CHANGED — the overlays below produce different output than the baseline." >&2
    echo "If that is the intended change, re-record with: tools/render-baseline.sh update" >&2
    echo >&2

    # Report each changed overlay with its actual manifest diff, not just the digest.
    diff "$baseline_file" "$current" \
        | sed -n 's/^[<>] [^ ][^ ]*  //p' \
        | LC_ALL=C sort -u \
        | while IFS= read -r overlay; do
            printf '=== %s\n' "$overlay" >&2
            before=$(grep "  $overlay\$" "$baseline_file" | cut -d' ' -f1 || true)
            after=$(grep "  $overlay\$" "$current" | cut -d' ' -f1 || true)
            if [ -z "$before" ]; then
                printf '  new overlay (not in baseline)\n' >&2
            elif [ -z "$after" ]; then
                printf '  overlay removed\n' >&2
            else
                printf '  %s -> %s\n' "$before" "$after" >&2
                printf '  inspect with: kubectl kustomize %s\n' "$overlay" >&2
            fi
        done
    exit 1
    ;;
*)
    echo "usage: tools/render-baseline.sh [check|update|list]" >&2
    exit 2
    ;;
esac
