#!/usr/bin/env bash
set -u

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SYNC_SCRIPT="$ROOT/packaging/lib/main-checkout-ff-sync.sh"
mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/overdeck"
TEST_ROOT="$(mktemp -d "${XDG_CACHE_HOME:-$HOME/.cache}/overdeck/main-checkout-ff-sync-test.XXXXXX")"
cleanup() { chmod -R u+w "$TEST_ROOT" 2>/dev/null || true; rm -rf "$TEST_ROOT"; }
trap cleanup EXIT

PASS=0
FAIL=0
ok() { PASS=$((PASS + 1)); printf 'ok - %s\n' "$1"; }
bad() { FAIL=$((FAIL + 1)); printf 'not ok - %s\n' "$1"; }

new_fixture() {
  local name=$1
  local root="$TEST_ROOT/$name"
  mkdir -p "$root/seed"
  /usr/bin/git -C "$root/seed" init -q
  /usr/bin/git -C "$root/seed" config user.name Fixture
  /usr/bin/git -C "$root/seed" config user.email fixture@example.invalid
  printf 'base\n' >"$root/seed/tracked"
  /usr/bin/git -C "$root/seed" add tracked
  /usr/bin/git -C "$root/seed" commit -qm base
  /usr/bin/git clone -q --bare "$root/seed" "$root/origin.git"
  /usr/bin/git -C "$root/seed" remote add origin "$root/origin.git"
  /usr/bin/git -C "$root/seed" push -q -u origin HEAD:main
  /usr/bin/git clone -q "$root/origin.git" "$root/main"
  /usr/bin/git clone -q "$root/origin.git" "$root/writer"
  /usr/bin/git -C "$root/writer" config user.name Fixture
  /usr/bin/git -C "$root/writer" config user.email fixture@example.invalid
  printf '%s\n' "$root"
}

advance_origin() {
  local root=$1 value=$2
  printf '%s\n' "$value" >>"$root/writer/tracked"
  /usr/bin/git -C "$root/writer" add tracked
  /usr/bin/git -C "$root/writer" commit -qm advance
  /usr/bin/git -C "$root/writer" push -q origin HEAD:main
}

run_sync() {
  local root=$1
  OVERDECK_MAIN_CHECKOUT="$root/main" \
    OVERDECK_MAIN_CHECKOUT_SYNC_DIR="$root/state" \
    bash -c 'source "$1"; main_checkout_ff_sync; printf "%s\n" "$MAIN_CHECKOUT_FF_SYNC_STATUS"' _ "$SYNC_SCRIPT"
}

last_log() { tail -n 1 "$1/state/attempts.jsonl"; }

clean_root=$(new_fixture clean-behind)
clean_before=$(/usr/bin/git -C "$clean_root/main" rev-parse HEAD)
advance_origin "$clean_root" remote-change
clean_result=$(run_sync "$clean_root")
clean_after=$(/usr/bin/git -C "$clean_root/main" rev-parse HEAD)
clean_remote=$(/usr/bin/git -C "$clean_root/main" rev-parse origin/main)
if [[ "$clean_result" == fast-forwarded && "$clean_after" == "$clean_remote" && "$clean_after" != "$clean_before" ]] \
  && last_log "$clean_root" | python3 -c 'import json, sys; x=json.load(sys.stdin); assert x["step"] == "main-checkout-ff-sync" and x["status"] == "fast-forwarded"'; then
  ok "fast-forwards a clean checkout that is behind origin/main"
else
  bad "fast-forwards a clean checkout that is behind origin/main"
fi

dirty_root=$(new_fixture dirty)
advance_origin "$dirty_root" remote-change
printf 'owner work\n' >"$dirty_root/main/owner-note"
dirty_before=$(/usr/bin/git -C "$dirty_root/main" rev-parse HEAD)
dirty_result=$(run_sync "$dirty_root")
dirty_after=$(/usr/bin/git -C "$dirty_root/main" rev-parse HEAD)
if [[ "$dirty_result" == refused && "$dirty_after" == "$dirty_before" && -f "$dirty_root/main/owner-note" ]] \
  && last_log "$dirty_root" | python3 -c 'import json, sys; x=json.load(sys.stdin); assert x["status"] == "refused" and x["blocking_path"] == "owner-note" and x["detail"] == "dirty"'; then
  ok "refuses dirty work without changing it"
else
  bad "refuses dirty work without changing it"
fi

diverged_root=$(new_fixture diverged)
advance_origin "$diverged_root" remote-change
printf 'local change\n' >>"$diverged_root/main/tracked"
/usr/bin/git -C "$diverged_root/main" add tracked
/usr/bin/git -C "$diverged_root/main" config user.name Fixture
/usr/bin/git -C "$diverged_root/main" config user.email fixture@example.invalid
/usr/bin/git -C "$diverged_root/main" commit -qm local
diverged_before=$(/usr/bin/git -C "$diverged_root/main" rev-parse HEAD)
diverged_result=$(run_sync "$diverged_root")
diverged_after=$(/usr/bin/git -C "$diverged_root/main" rev-parse HEAD)
if [[ "$diverged_result" == refused && "$diverged_after" == "$diverged_before" ]] \
  && last_log "$diverged_root" | python3 -c 'import json, sys; x=json.load(sys.stdin); assert x["status"] == "refused" and x["blocking_path"] == ".git/HEAD" and x["detail"] == "diverged"'; then
  ok "refuses a diverged checkout without rewriting it"
else
  bad "refuses a diverged checkout without rewriting it"
fi

lock_root=$(new_fixture index-lock)
advance_origin "$lock_root" remote-change
: >"$lock_root/main/.git/index.lock"
lock_before=$(/usr/bin/git -C "$lock_root/main" rev-parse HEAD)
lock_result=$(run_sync "$lock_root")
lock_after=$(/usr/bin/git -C "$lock_root/main" rev-parse HEAD)
if [[ "$lock_result" == refused && "$lock_after" == "$lock_before" && -e "$lock_root/main/.git/index.lock" ]] \
  && last_log "$lock_root" | python3 -c 'import json, sys; x=json.load(sys.stdin); assert x["status"] == "refused" and x["blocking_path"] == ".git/index.lock" and x["detail"] == "index-lock-present"'; then
  ok "refuses while another Git process owns index.lock"
else
  bad "refuses while another Git process owns index.lock"
fi

if ! sed '/^[[:space:]]*#/d' "$SYNC_SCRIPT" | grep -Eq '(^|[[:space:]])(reset|checkout|clean|stash)([[:space:]]|$)'; then
  ok "the sync step contains no destructive Git verb"
else
  bad "the sync step contains no destructive Git verb"
fi

printf 'PASS=%s FAIL=%s\n' "$PASS" "$FAIL"
(( FAIL == 0 ))
