#!/usr/bin/env bash
# test-pr-create-fallback-default-base.sh — hermetic test for the API-fallback
# base resolution in pr-create.sh (T51-P2 WP5a / spec E4).
#
# The API-fallback payload historically hardcoded "base": "main", mistargeting
# every fallback PR on repos whose trunk is not main (e.g. mosaicstack/stack,
# default branch "next"). The fix: an explicit -B always wins; with none, the
# base is resolved from the provider API default_branch, and a failed
# resolution fails loud instead of guessing.
#
# Hermetic by construction: every curl invocation is a PATH-first stub; the
# fixture repo's remote is git.example.test (never dialed); HOME is a sandbox
# with no tea config (so the wrapper takes the API fallback path); GITEA_TOKEN
# comes from the environment. No real forge is contacted.

# shellcheck disable=SC2317
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-create-fallback-base}"

PASS=0 FAIL=0 FAILED_CASES=""

ok()  { PASS=$((PASS + 1)); }
bad() { FAIL=$((FAIL + 1)); FAILED_CASES="$FAILED_CASES $1"; printf 'FAIL: %s\n' "$1" >&2; }

assert_rc()    { local d="$1" e="$2" a="$3"; [ "$e" = "$a" ] && ok || bad "$d (expected rc=$e got rc=$a)"; }
assert_eq()    { local d="$1" e="$2" a="$3"; [ "$e" = "$a" ] && ok || bad "$d (expected [$e] got [$a])"; }
assert_contains() { local d="$1" h="$2" n="$3"; case "$h" in *"$n"*) ok ;; *) bad "$d (missing [$n])" ;; esac; }

json_field() { # $1 payload file, $2 field
    python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2], ""))' "$1" "$2"
}

rm -rf "$WORK_DIR"

# ---- fixture -----------------------------------------------------------------
ROOT="$WORK_DIR/fixture"
TOOLS="$ROOT/tools/git"
mkdir -p "$TOOLS" "$ROOT/repo" "$ROOT/home" "$ROOT/stub"
cp "$SCRIPT_DIR/pr-create.sh" "$TOOLS/pr-create.sh"
cp "$SCRIPT_DIR/detect-platform.sh" "$TOOLS/detect-platform.sh"

git -C "$ROOT/repo" init -q -b fix/e4
git -C "$ROOT/repo" -c user.name=fixture -c user.email=fixture@test commit -q --allow-empty -m base
git -C "$ROOT/repo" remote add origin https://git.example.test/acme/widgets.git

# curl stub: GET repo -> default_branch JSON (or failure mode); POST pulls ->
# capture payload, answer with a minimal PR JSON. Every call is logged.
cat > "$ROOT/stub/curl" <<STUB
#!/usr/bin/env bash
set -u
mode="\${CURL_STUB_GET_MODE:-ok}"
printf '%s\n' "\$*" >> "$ROOT/curl-calls.log"
url="\${!#}"
if [[ "\$url" == */api/v1/repos/acme/widgets ]]; then
    # repo GET (default-branch resolution). Modes cover the value shapes the
    # resolver must accept or refuse (T51P2WP5AR B2/B3).
    case "\$mode" in
        ok)         printf '%s\n' '{"id":1,"default_branch":"next","full_name":"acme/widgets"}' ;;
        fail)       echo "curl stub: simulated repo lookup failure" >&2; exit 1 ;;
        fail-json)  printf '%s\n' '{"default_branch":"next"}'; exit 22 ;;
        null)       printf '%s\n' '{"default_branch":null}' ;;
        numeric)    printf '%s\n' '{"default_branch":7}' ;;
        blank)      printf '%s\n' '{"default_branch":"   "}' ;;
        *)          echo "curl stub: unknown GET mode \$mode" >&2; exit 1 ;;
    esac
    exit 0
fi
if [[ "\$url" == */api/v1/repos/acme/widgets/pulls ]]; then
    # PR POST: capture the payload, emit a PR-shaped answer
    while [[ \$# -gt 0 ]]; do
        case "\$1" in
            -d) printf '%s' "\$2" > "$ROOT/payload.json"; shift 2 ;;
            *) shift ;;
        esac
    done
    printf '%s\n' '{"number":42,"html_url":"https://git.example.test/acme/widgets/pulls/42"}'
    exit 0
fi
echo "curl stub: unexpected URL \$url" >&2
exit 1
STUB
chmod +x "$ROOT/stub/curl"

run_pr_create() { # args... -> sets RC/OUT/ERR
    RC=0
    OUT=$(cd "$ROOT/repo" && env -i \
        PATH="$ROOT/stub:/usr/bin:/bin" \
        HOME="$ROOT/home" \
        GITEA_TOKEN=stub-token \
        bash "$TOOLS/pr-create.sh" "$@" 2>"$ROOT/err.txt")
    RC=$?
    ERR="$(cat "$ROOT/err.txt")"
}

calls_matching() { grep -c -- "$1" "$ROOT/curl-calls.log" 2>/dev/null || true; }

echo "== (1) no -B: fallback base resolves to the forge default branch, not main =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing"
assert_rc "rc" 0 "$RC"
assert_contains "API fallback path taken (tea login unresolvable in fixture)" "$ERR" "trying Gitea API fallback"
assert_eq "repo GET performed" 1 "$(calls_matching '/api/v1/repos/acme/widgets$')"
assert_eq "POST performed" 1 "$(calls_matching '/pulls$')"
assert_eq "payload base is forge default (next)" "next" "$(json_field "$ROOT/payload.json" base)"
assert_eq "payload head" "fix/e4" "$(json_field "$ROOT/payload.json" head)"
assert_eq "payload title" "fix thing" "$(json_field "$ROOT/payload.json" title)"

echo "== (2) explicit -B wins; the default branch is not consulted =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing" -B release/1.x
assert_rc "rc" 0 "$RC"
assert_eq "repo GET not consulted for explicit base" 0 "$(calls_matching '/api/v1/repos/acme/widgets$')"
assert_eq "payload base is the explicit -B" "release/1.x" "$(json_field "$ROOT/payload.json" base)"

echo "== (3) default-branch lookup failure: loud refusal, no POST =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
    PATH="$ROOT/stub:/usr/bin:/bin" \
    HOME="$ROOT/home" \
    GITEA_TOKEN=stub-token \
    CURL_STUB_GET_MODE=fail \
    bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
assert_rc "nonzero rc on unresolvable base" 1 "$RC"
assert_contains "loud error names -B" "$ERR" "could not resolve the forge default branch"
assert_contains "error names the remedy" "$ERR" "pass -B <branch> explicitly"
assert_eq "no POST issued" 0 "$(calls_matching '/pulls$')"

echo "== (4) payload never contains the literal fallback main =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing"
assert_rc "rc" 0 "$RC"
assert_eq "base field is next, never main" "next" "$(json_field "$ROOT/payload.json" base)"

echo "== (5) B2: HTTP failure with parseable JSON on stdout is a FAILED resolution =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
    PATH="$ROOT/stub:/usr/bin:/bin" \
    HOME="$ROOT/home" \
    GITEA_TOKEN=stub-token \
    CURL_STUB_GET_MODE=fail-json \
    bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
assert_rc "nonzero rc on HTTP failure despite valid JSON" 1 "$RC"
assert_contains "loud error names -B" "$ERR" "could not resolve the forge default branch"
assert_contains "error names the remedy" "$ERR" "pass -B <branch> explicitly"
assert_eq "no POST issued" 0 "$(calls_matching '/pulls$')"

echo "== (6) B3: null / numeric / blank default_branch are failed resolutions =="
for bad in null numeric blank; do
    : > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
    RC=0
    OUT=$(cd "$ROOT/repo" && env -i \
        PATH="$ROOT/stub:/usr/bin:/bin" \
        HOME="$ROOT/home" \
        GITEA_TOKEN=stub-token \
        CURL_STUB_GET_MODE="$bad" \
        bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
    RC=$?
    ERR="$(cat "$ROOT/err.txt")"
    assert_rc "B3 $bad: nonzero rc" 1 "$RC"
    assert_contains "B3 $bad: loud error" "$ERR" "could not resolve the forge default branch"
    assert_eq "B3 $bad: no POST issued" 0 "$(calls_matching '/pulls$')"
done

echo
echo "pass=$PASS fail=$FAIL"
if [ "$FAIL" -gt 0 ]; then
    echo "FAILED CASES:$FAILED_CASES"
    exit 1
fi
echo "ALL GREEN"
