#!/usr/bin/env bash
# P0 gate. A milestone is done when this exits 0 - never when it looks done.
#
# Asserts the guarantees P0 claims, against the RUNNING deployment:
#   1. app is healthy and reports its version
#   2. reference data is seeded from Mike's real spreadsheets
#   3. ticket numbers strictly increase
#   4. ticket numbers survive a container restart (durable, non-resetting)
#   5. tickets cannot be deleted
#   6. an uploaded photo is stored, retrievable through the authorised route,
#      and NOT reachable as a static file
#   7. attribution is required for state-changing calls
#
# Usage: main/scripts/gate-p0.sh
set -uo pipefail

BASE="${CP_BASE:-http://172.17.0.1:3140}"
DB_CONTAINER="${CP_DB_CONTAINER:-coachpapa-db}"
APP_CONTAINER="${CP_APP_CONTAINER:-coachpapa-app}"
VERSION_FILE="$(dirname "$0")/../VERSION"

pass=0
fail=0

ok()   { printf '  PASS  %s\n' "$1"; pass=$((pass + 1)); }
bad()  { printf '  FAIL  %s\n' "$1"; fail=$((fail + 1)); }
check() { if [ "$2" = "$3" ]; then ok "$1"; else bad "$1 (expected '$3', got '$2')"; fi; }

api() { curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$@"; }
jqf() { python3 -c "import sys,json;d=json.load(sys.stdin);print($1)"; }

echo "P0 gate -> $BASE"

# --- 1. health + version ----------------------------------------------------
check "healthz returns 200" "$(api "$BASE/healthz")" "200"
expected_version="$(tr -d '[:space:]' < "$VERSION_FILE")"
reported_version="$(curl -s --max-time 10 "$BASE/api/meta" | jqf "d['version']")"
check "/api/meta version matches VERSION" "$reported_version" "$expected_version"

# --- 2. seeded reference data ----------------------------------------------
asset_count="$(docker exec "$DB_CONTAINER" psql -U coachpapa -d coachpapa -tAc \
  "select count(*) from assets" 2>/dev/null | tr -d '[:space:]')"
check "376 assets seeded from the Britco equipment list" "$asset_count" "376"

part_count="$(docker exec "$DB_CONTAINER" psql -U coachpapa -d coachpapa -tAc \
  "select count(*) from parts" 2>/dev/null | tr -d '[:space:]')"
if [ "${part_count:-0}" -gt 400 ]; then
  ok "parts seeded ($part_count)"
else
  bad "parts seeded (expected >400, got ${part_count:-0})"
fi

# --- actor for the mutating calls -------------------------------------------
actor="$(curl -s --max-time 10 "$BASE/api/technicians" | jqf "d[0]['id']")"

create_ticket() {
  curl -s --max-time 20 -X POST "$BASE/api/tickets" \
    -H "X-CP-Actor: $actor" \
    -F "payload={\"description\":\"$1\",\"asset_id\":null,\"priority\":\"normal\"}" \
    "${@:2}"
}

# --- 3. numbers strictly increase -------------------------------------------
n1="$(create_ticket "gate-p0 first" | jqf "d['number']")"
n2="$(create_ticket "gate-p0 second" | jqf "d['number']")"
if [ "$n2" -gt "$n1" ]; then
  ok "ticket numbers strictly increase ($n1 -> $n2)"
else
  bad "ticket numbers strictly increase ($n1 -> $n2)"
fi

# --- 4. numbers survive a restart -------------------------------------------
docker restart "$APP_CONTAINER" >/dev/null 2>&1
for _ in $(seq 1 30); do
  [ "$(api "$BASE/healthz")" = "200" ] && break
  sleep 2
done
n3="$(create_ticket "gate-p0 after restart" | jqf "d['number']")"
if [ "$n3" -gt "$n2" ]; then
  ok "numbering is durable across restart ($n2 -> $n3)"
else
  bad "numbering is durable across restart ($n2 -> $n3)"
fi

# --- 5. tickets cannot be deleted -------------------------------------------
ticket_id="$(curl -s --max-time 10 "$BASE/api/tickets?open_only=false" | jqf "d[0]['id']")"
check "DELETE on a ticket is refused" \
  "$(api -X DELETE -H "X-CP-Actor: $actor" "$BASE/api/tickets/$ticket_id")" "405"

# --- 6. media is stored and only served through the authorised route --------
tmp_png="$(mktemp /tmp/gate-p0-XXXX.png)"
python3 -c "
from PIL import Image
Image.new('RGB', (48, 48), (31, 111, 235)).save('$tmp_png')
" 2>/dev/null || printf '\x89PNG\r\n\x1a\n' > "$tmp_png"

media_json="$(curl -s --max-time 20 -X POST "$BASE/api/tickets/$ticket_id/attachments" \
  -H "X-CP-Actor: $actor" -F "attachment=@$tmp_png;type=image/png")"
media_id="$(printf '%s' "$media_json" | jqf "d['media'][-1]['id']" 2>/dev/null)"

if [ -n "${media_id:-}" ]; then
  ok "photo upload accepted (media id $media_id)"
  check "media served through the authorised route" \
    "$(api -H "X-CP-Actor: $actor" "$BASE/api/media/$media_id")" "200"
  check "media refused without an actor" "$(api "$BASE/api/media/$media_id")" "401"

  rel="$(docker exec "$DB_CONTAINER" psql -U coachpapa -d coachpapa -tAc \
    "select rel_path from ticket_media where id=$media_id" | tr -d '[:space:]')"
  # The stored path must not be fetchable as a static asset under any prefix.
  # Status alone is not the test: the SPA catch-all legitimately answers 200
  # with the HTML shell for unknown paths. What must never come back is the
  # image itself, so assert on the content type.
  static_leak=0
  for prefix in "" "/media" "/assets" "/static" "/data" "/../data/media"; do
    ctype="$(curl -s -o /dev/null -w '%{content_type}' --max-time 5 "$BASE$prefix/$rel")"
    case "$ctype" in
      image/*) static_leak=1 ;;
    esac
  done
  check "media is NOT reachable as a static file" "$static_leak" "0"
else
  bad "photo upload accepted (response: $(printf '%s' "$media_json" | head -c 200))"
fi
rm -f "$tmp_png"

# --- 7. attribution required for state changes ------------------------------
check "creating a ticket without an actor is refused" \
  "$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 -X POST "$BASE/api/tickets" \
      -F 'payload={"description":"no actor","asset_id":null,"priority":"normal"}')" "401"

# --- cleanup ----------------------------------------------------------------
# The gate creates real tickets and they can never be deleted - that is the
# guarantee under test. Void them instead so a demo view stays clean; the
# numbers stay consumed, which is exactly right.
for n in "$n1" "$n2" "$n3"; do
  id="$(docker exec "$DB_CONTAINER" psql -U coachpapa -d coachpapa -tAc \
    "select id from tickets where number=$n" | tr -d '[:space:]')"
  [ -n "$id" ] && curl -s -o /dev/null --max-time 10 -X PATCH "$BASE/api/tickets/$id" \
    -H "X-CP-Actor: $actor" -H 'Content-Type: application/json' \
    -d '{"status":"void"}'
done

echo
echo "P0 gate: $pass passed, $fail failed"
[ "$fail" -eq 0 ] || exit 1
