#!/usr/bin/env bash
# D2 smoke — proves the Day 2 acceptance gate end-to-end against the
# live MinIO at :9000 and the dedicated bucket "lakehouse-go-primary".
#
# Validates:
#   - PUT a small file → 200, body roundtrips on GET, appears in LIST
#   - DELETE removes the key, subsequent GET → 404
#   - PUT exceeding 256 MiB → 413 Payload Too Large
#   - 5th concurrent PUT (4-slot semaphore full) → 503 + Retry-After:5
#
# Usage:  ./scripts/d2_smoke.sh

set -euo pipefail
cd "$(dirname "$0")/.."

export PATH="$PATH:/usr/local/go/bin"

echo "[d2-smoke] building storaged..."
go build -o bin/ ./cmd/storaged

# Cleanup any prior storaged process on :3211 before launching.
pkill -f "bin/storaged" 2>/dev/null || true

STORAGED_PID=""
SLOW_PIDS=()
TMP="$(mktemp -d)"
cleanup() {
  echo "[d2-smoke] cleanup"
  if [ -n "$STORAGED_PID" ]; then
    kill "$STORAGED_PID" 2>/dev/null || true
  fi
  if [ ${#SLOW_PIDS[@]} -gt 0 ]; then
    kill "${SLOW_PIDS[@]}" 2>/dev/null || true
  fi
  rm -rf "$TMP"
}
trap cleanup EXIT INT TERM

echo "[d2-smoke] launching storaged..."
./bin/storaged > /tmp/storaged.log 2>&1 &
STORAGED_PID=$!

# Poll /health up to 5s — same discipline as d1_smoke.
deadline=$(($(date +%s) + 5))
while [ "$(date +%s)" -lt "$deadline" ]; do
  if curl -sS --max-time 1 http://127.0.0.1:3211/health >/dev/null 2>&1; then
    break
  fi
  sleep 0.05
done
if ! curl -sS --max-time 1 http://127.0.0.1:3211/health >/dev/null 2>&1; then
  echo "  [d2-smoke] storaged failed to bind within 5s — log:"
  tail -10 /tmp/storaged.log | sed 's/^/    /'
  exit 1
fi

FAILED=0
KEY="d2-smoke/$(date +%s).bin"

echo "[d2-smoke] PUT round-trip:"
printf "hello-d2-smoke" > "$TMP/sample.bin"
HTTP="$(curl -sS -o "$TMP/put.out" -w '%{http_code}' -X PUT --data-binary @"$TMP/sample.bin" "http://127.0.0.1:3211/storage/put/$KEY")"
if [ "$HTTP" = "200" ]; then
  echo "  ✓ PUT $KEY → 200"
else
  echo "  ✗ PUT $KEY → $HTTP (body=$(cat "$TMP/put.out"))"
  FAILED=1
fi

echo "[d2-smoke] GET echoes bytes:"
curl -sS -o "$TMP/get.out" "http://127.0.0.1:3211/storage/get/$KEY"
if cmp -s "$TMP/sample.bin" "$TMP/get.out"; then
  echo "  ✓ GET $KEY → bytes match"
else
  echo "  ✗ GET $KEY → bytes differ"
  FAILED=1
fi

echo "[d2-smoke] LIST includes key:"
LISTED="$(curl -sS "http://127.0.0.1:3211/storage/list?prefix=d2-smoke/" | grep -o "\"$KEY\"" || true)"
if [ -n "$LISTED" ]; then
  echo "  ✓ LIST prefix=d2-smoke/ → contains $KEY"
else
  echo "  ✗ LIST prefix=d2-smoke/ → missing $KEY"
  FAILED=1
fi

echo "[d2-smoke] DELETE then GET → 404:"
curl -sS -o /dev/null -X DELETE "http://127.0.0.1:3211/storage/delete/$KEY"
HTTP="$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1:3211/storage/get/$KEY")"
if [ "$HTTP" = "404" ]; then
  echo "  ✓ DELETE then GET → 404"
else
  echo "  ✗ DELETE then GET → $HTTP (expected 404)"
  FAILED=1
fi

echo "[d2-smoke] 256 MiB cap → 413:"
dd if=/dev/zero of="$TMP/big.bin" bs=1M count=257 status=none
HTTP="$(curl -sS -o /dev/null -w '%{http_code}' -X PUT --data-binary @"$TMP/big.bin" "http://127.0.0.1:3211/storage/put/d2-smoke/oversize.bin")"
if [ "$HTTP" = "413" ]; then
  echo "  ✓ PUT 257 MiB → 413"
else
  echo "  ✗ PUT 257 MiB → $HTTP (expected 413)"
  FAILED=1
fi

echo "[d2-smoke] semaphore: 5th concurrent PUT → 503 + Retry-After:5"
# Streaming uploads: -T file with --limit-rate makes curl actually
# pace the body chunks as bytes ship — unlike --data-binary @- which
# buffers stdin before opening the connection.
dd if=/dev/zero of="$TMP/slow.bin" bs=1M count=100 status=none

slow_put() {
  local idx="$1"
  local out="$TMP/slow_${idx}.code"
  curl -sS -o /dev/null -w "%{http_code}" -X PUT \
    -T "$TMP/slow.bin" --limit-rate 5M \
    "http://127.0.0.1:3211/storage/put/d2-smoke-cap-${idx}.bin" > "$out" 2>&1 || echo CURL_FAIL > "$out"
}

for i in 1 2 3 4; do
  slow_put "$i" &
  SLOW_PIDS+=($!)
done

# Give the 4 slow uploads ~750ms to actually start streaming + sit
# on the semaphore. At 5 MiB/s for 100 MiB they'll each run ~20s,
# so a brief warmup is plenty.
sleep 0.75

# Capture full headers on the 5th request to read Retry-After.
curl -sS -i -o "$TMP/blocked.out" -w '%{http_code}' -X PUT \
  --data-binary "blocked" \
  "http://127.0.0.1:3211/storage/put/d2-smoke-blocked.bin" > "$TMP/blocked.code" 2>/dev/null || true
HTTP_BLOCKED="$(cat "$TMP/blocked.code")"
RA="$(grep -i '^Retry-After:' "$TMP/blocked.out" | awk '{print $2}' | tr -d '\r' || true)"

if [ "$HTTP_BLOCKED" = "503" ] && [ "$RA" = "5" ]; then
  echo "  ✓ 5th concurrent PUT → 503 + Retry-After: 5"
else
  echo "  ✗ 5th concurrent PUT → code=$HTTP_BLOCKED retry-after=$RA"
  echo "    (slow PUT codes: $(for i in 1 2 3 4; do printf '%s ' "$(cat "$TMP/slow_${i}.code" 2>/dev/null || echo ?)"; done))"
  FAILED=1
fi

# Drain the 4 slow PUTs cleanly. Don't wait on STORAGED_PID — that
# only exits on signal. Wait only on the slow PUT PIDs we spawned.
for pid in "${SLOW_PIDS[@]}"; do
  wait "$pid" 2>/dev/null || true
done
SLOW_PIDS=()

# Cleanup smoke keys regardless of pass/fail.
for i in 1 2 3 4; do
  curl -sS -o /dev/null -X DELETE "http://127.0.0.1:3211/storage/delete/d2-smoke-cap-${i}.bin" || true
done
curl -sS -o /dev/null -X DELETE "http://127.0.0.1:3211/storage/delete/d2-smoke-blocked.bin" || true
curl -sS -o /dev/null -X DELETE "http://127.0.0.1:3211/storage/delete/d2-smoke/oversize.bin" || true

if [ "$FAILED" -eq 0 ]; then
  echo "[d2-smoke] D2 acceptance gate: PASSED"
  exit 0
else
  echo "[d2-smoke] D2 acceptance gate: FAILED"
  exit 1
fi
