#!/usr/bin/env bash
# scripts/migration-check — detect duplicate forward-migration NUMBERS across
# `main` + every worktree, before two branches collide on ship.
#
# Forward-only migrations (ADR 0005) are numbered `NNNN_slug.sql`. Parallel
# worktrees each mint the next number off the same `main`, so two of them
# routinely grab the SAME number for DIFFERENT migrations — the collision only
# surfaces when the second one ships (duplicate number, ledger confusion). This
# scans the working tree of `main` (the primary checkout) and every sibling
# worktree and flags any number that maps to 2+ distinct slugs.
#
# Exit 1 on collision (usable as a ship gate); 0 when clean. `--quiet` prints
# only on collision (for soft/advisory wiring).
set -euo pipefail
cd "$(dirname "$0")/.."

QUIET=0
[[ "${1:-}" == "--quiet" ]] && QUIET=1

MAIN_ROOT="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"
MIGDIR="src/precis/migrations"

rows="$(mktemp)"
trap 'rm -f "$rows"' EXIT

n_trees=0
for wt in "$MAIN_ROOT" "$MAIN_ROOT"/.claude/worktrees/*; do
    [[ -d "$wt/$MIGDIR" ]] || continue
    name="$(basename "$wt")"; [[ "$wt" == "$MAIN_ROOT" ]] && name="main"
    n_trees=$((n_trees + 1))
    for f in "$wt/$MIGDIR"/[0-9][0-9][0-9][0-9]_*.sql; do
        [[ -e "$f" ]] || continue
        num="$(basename "$f" | grep -oE '^[0-9]{4}')"
        printf '%s\t%s\t%s\n' "$num" "$name" "$(basename "$f")"
    done
done >> "$rows"

# Branchless branches (docs/backlog/inflight-sessionless-branches.md): a
# branch git's worktree admin has no directory for at all is invisible to the
# filesystem walk above — its migrations/*.sql only exist inside the branch's
# own git tree, read via `git show`/`ls-tree`, never a working directory.
# Same skip list as scripts/inflight: main itself, `ci/*` (scripts/ship
# --remote's throwaway gate pushes), and any branch a live worktree is
# actually sitting on.
MAIN_BRANCH="$(git -C "$MAIN_ROOT" symbolic-ref --quiet --short HEAD 2>/dev/null || echo main)"
LIVE_BRANCHES=$'\n'"$MAIN_BRANCH"$'\n'
for wt in "$MAIN_ROOT" "$MAIN_ROOT"/.claude/worktrees/*; do
    [[ -d "$wt" ]] || continue
    b="$(git -C "$wt" symbolic-ref --quiet --short HEAD 2>/dev/null)" || continue
    LIVE_BRANCHES="${LIVE_BRANCHES}${b}"$'\n'
done

n_branches=0
while IFS= read -r branch; do
    [[ -z "$branch" ]] && continue
    case "$branch" in ci/*) continue ;; esac
    case $LIVE_BRANCHES in *$'\n'"$branch"$'\n'*) continue ;; esac
    have_any=0
    while IFS= read -r rel; do
        [[ -z "$rel" ]] && continue
        base="$(basename "$rel")"
        # Top-level only, like the filesystem walk: archive/ and baseline/
        # carry retired numbers that every branch has and main does not list.
        [[ "$rel" == "$MIGDIR/$base" ]] || continue
        [[ "$base" =~ ^[0-9]{4}_ ]] || continue
        num="${base:0:4}"
        have_any=1
        printf '%s\t%s\t%s\n' "$num" "$branch" "$base"
    done < <(git -C "$MAIN_ROOT" ls-tree -r --name-only "$branch" -- "$MIGDIR" 2>/dev/null)
    [[ "$have_any" == 1 ]] && n_branches=$((n_branches + 1))
done < <(git -C "$MAIN_ROOT" for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null) >> "$rows"

# A collision worth flagging = a worktree introduces a slug at a number BEYOND
# what main already carries. Main's own historical duplicate numbers (e.g. two
# 0037_* shipped long ago) are accepted reality — every worktree carries them,
# so they are NOT flagged. Rule per number: collision if the distinct-slug count
# across all trees exceeds max(main's distinct-slug count, 1).
collisions="$(awk -F'\t' '
    {
        pair = $1 SUBSEP $3
        all[pair] = 1
        if ($2 == "main") main[pair] = 1
        where[pair] = where[pair] " " $2
    }
    END {
        for (p in all)  { split(p, a, SUBSEP); ntot[a[1]]++ }
        for (p in main) { split(p, a, SUBSEP); nmain[a[1]]++ }
        for (num in ntot) {
            floor = (nmain[num] > 1) ? nmain[num] : 1
            if (ntot[num] > floor) {
                print num ":"
                for (p in all) {
                    split(p, a, SUBSEP)
                    if (a[1] == num && !(p in main))
                        print "    NEW   " a[2] "  (in" where[p] " )"
                }
                for (p in main) {
                    split(p, a, SUBSEP)
                    if (a[1] == num) print "    main  " a[2]
                }
            }
        }
    }' "$rows")"

if [[ -n "$collisions" ]]; then
    echo "✖ migration NUMBER collision (same number, different slug across trees):"
    echo "$collisions"
    echo
    echo "→ renumber the UNSHIPPED migration(s) to the next free number above"
    echo "  main's max before that worktree ships."
    exit 1
fi

if [[ "$QUIET" != 1 ]]; then
    msg="✓ migration numbers: no collisions across main + $((n_trees - 1)) worktree(s)"
    [[ "$n_branches" -gt 0 ]] && msg="$msg + $n_branches branchless branch(es)"
    echo "$msg"
fi
