#!/usr/bin/env bash
# Runs Phase 10 verification checks from docs/plans/phase-10-plan.md.
#
# Covers cover sheet generation, lookup, list filters, PDF endpoints,
# barcode-assisted batch upload, redeem/auto-assign, and reuse prevention.
#
# Prerequisites:
#   docker compose up -d          (PostgreSQL, Redis, MinIO)
#   dotnet ef database update --project VigilCareRecordsAPI
#   dotnet run --project VigilCareRecordsAPI
#   Phase 1–9 seed data (intake1, entry1, admin1)
#
# Environment overrides:
#   VIGILCARE_API_URL          default: http://localhost:5217
#   VIGILCARE_COMPOSE_FILE     default: <repo>/docker-compose.yml
#   VIGILCARE_PG_HOST          default: localhost
#   VIGILCARE_PG_PORT          default: 5437
#   VIGILCARE_PG_DB            default: vigilcare_records
#   VIGILCARE_PG_USER          default: postgres
#   VIGILCARE_PG_PASSWORD      default: password
#   VIGILCARE_SKIP_DB_CHECKS   set to 1 to skip PostgreSQL assertions
#   VIGILCARE_SKIP_TEST_CHECKS set to 1 to skip dotnet integration tests
#
# Usage:
#   chmod +x scripts/run-vigilcare-records-phase-10-verification.sh
#   ./scripts/run-vigilcare-records-phase-10-verification.sh

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
FIXTURE_PDF="$SCRIPT_DIR/fixtures/test-scan.pdf"

API_URL="${VIGILCARE_API_URL:-http://localhost:5217}"
COMPOSE_FILE="${VIGILCARE_COMPOSE_FILE:-$REPO_ROOT/docker-compose.yml}"
COMPOSE=(docker compose -f "$COMPOSE_FILE")
PG_HOST="${VIGILCARE_PG_HOST:-localhost}"
PG_PORT="${VIGILCARE_PG_PORT:-5437}"
PG_DB="${VIGILCARE_PG_DB:-vigilcare_records}"
PG_USER="${VIGILCARE_PG_USER:-postgres}"
PG_PASSWORD="${VIGILCARE_PG_PASSWORD:-password}"
SKIP_DB_CHECKS="${VIGILCARE_SKIP_DB_CHECKS:-0}"
SKIP_TEST_CHECKS="${VIGILCARE_SKIP_TEST_CHECKS:-0}"

# Resolved at runtime from the live API (seed IDs vary per environment)
ENTRY_CLERK1_ID=""
PATIENT1_ID=""

PASS_COUNT=0
FAIL_COUNT=0
FAILED_TESTS=()

# Shared state populated during the run
INTAKE_TOKEN=""
ADMIN_TOKEN=""
ENTRY_TOKEN=""
UPLOAD_CODE=""
UPLOAD_COVER_SHEET_ID=""
UPLOAD_BATCH_ID=""
ASSIGN_CODE=""
PDF_SHEET_IDS=()

log() {
  printf '%s\n' "$*"
}

section() {
  log ""
  log "== $1 =="
}

pass() {
  PASS_COUNT=$((PASS_COUNT + 1))
  log "  PASS: $1"
}

fail() {
  FAIL_COUNT=$((FAIL_COUNT + 1))
  FAILED_TESTS+=("$1")
  log "  FAIL: $1"
}

require_cmd() {
  local cmd="$1"
  if ! command -v "$cmd" >/dev/null 2>&1; then
    log "ERROR: required command not found: $cmd"
    exit 1
  fi
}

ensure_fixture_pdf() {
  if [[ -f "$FIXTURE_PDF" ]]; then
    return 0
  fi
  mkdir -p "$(dirname "$FIXTURE_PDF")"
  cat >"$FIXTURE_PDF" <<'EOF'
%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<<>>>>endobj
xref
0 4
0000000000 65535 f 
0000000009 00000 n 
0000000058 00000 n 
0000000115 00000 n 
trailer<</Size 4/Root 1 0 R>>
startxref
206
%%EOF
EOF
}

unique_pdf_path() {
  local suffix="$1"
  local path="/tmp/vigilcare-p10-${suffix}-${RANDOM}.pdf"
  printf '%%PDF-1.4\nphase10-%s-%s\n%%%%EOF\n' "$suffix" "$(date +%s%N)" >"$path"
  printf '%s' "$path"
}

http_code() {
  curl -sS -o /dev/null -w '%{http_code}' "$@"
}

json_post() {
  local url="$1"
  local body="$2"
  local token="${3:-}"
  if [[ -n "$token" ]]; then
    curl -sS -X POST "$url" \
      -H "Authorization: Bearer $token" \
      -H 'Content-Type: application/json' \
      -d "$body"
  else
    curl -sS -X POST "$url" \
      -H 'Content-Type: application/json' \
      -d "$body"
  fi
}

json_get() {
  local url="$1"
  local token="$2"
  curl -sS "$url" -H "Authorization: Bearer $token"
}

login() {
  local username="$1"
  local password="${2:-password}"
  json_post "$API_URL/api/v1/auth/login" \
    "{\"username\":\"$username\",\"password\":\"$password\"}"
}

extract_data_field() {
  local json="$1"
  local field="$2"
  jq -er ".data.$field // empty" <<<"$json"
}

extract_bool_field() {
  local json="$1"
  local field="$2"
  jq -er ".data.$field | if . == null then empty else tostring end" <<<"$json"
}

resolve_directory_ids() {
  local users_json patients_json
  users_json="$(json_get "$API_URL/api/v1/users?role=DATA_ENTRY_CLERK" "$INTAKE_TOKEN")"
  patients_json="$(json_get "$API_URL/api/v1/patients/search?q=Patient" "$INTAKE_TOKEN")"

  ENTRY_CLERK1_ID="$(jq -er '.data[0].id // empty' <<<"$users_json" 2>/dev/null || true)"
  PATIENT1_ID="$(jq -er '.data[0].id // empty' <<<"$patients_json" 2>/dev/null || true)"

  if [[ -n "$ENTRY_CLERK1_ID" ]]; then
    pass "resolved entry clerk ID for auto-assign test"
  else
    fail "resolved entry clerk ID for auto-assign test"
  fi

  if [[ -n "$PATIENT1_ID" ]]; then
    pass "resolved patient ID for patient-linked cover sheet test"
  else
    fail "resolved patient ID for patient-linked cover sheet test"
  fi
}

extract_error_code() {
  local json="$1"
  jq -er '.error.code // empty' <<<"$json" 2>/dev/null || true
}

assert_api_reachable() {
  local code
  code="$(http_code "$API_URL/swagger/index.html" || true)"
  if [[ "$code" != "200" ]]; then
    log "ERROR: API not reachable at $API_URL (HTTP $code)."
    log "Start infrastructure with: docker compose up -d"
    log "Apply migrations with: dotnet ef database update --project VigilCareRecordsAPI"
    log "Start API with: dotnet run --project VigilCareRecordsAPI"
    exit 1
  fi
}

compose_service_running() {
  local service="$1"
  "${COMPOSE[@]}" ps --status running --services 2>/dev/null | grep -qx "$service"
}

psql_available() {
  [[ "$SKIP_DB_CHECKS" == "1" ]] && return 1
  compose_service_running postgres && return 0
  command -v psql >/dev/null 2>&1 && return 0
  return 1
}

psql_query() {
  if [[ "$SKIP_DB_CHECKS" == "1" ]]; then
    return 1
  fi
  if compose_service_running postgres; then
    "${COMPOSE[@]}" exec -T postgres \
      psql -U "$PG_USER" -d "$PG_DB" -Atqc "$1"
  elif command -v psql >/dev/null 2>&1; then
    PGPASSWORD="$PG_PASSWORD" psql -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DB" -Atqc "$1"
  else
    return 1
  fi
}

upload_with_cover_sheet() {
  local token="$1"
  local code="$2"
  local pdf="$3"
  curl -sS -X POST "$API_URL/api/v1/digitization-batches" \
    -H "Authorization: Bearer $token" \
    -F "file=@${pdf};type=application/pdf" \
    -F "coverSheetCode=$code"
}

upload_with_cover_sheet_status() {
  local token="$1"
  local code="$2"
  local pdf="$3"
  local body_file http_code_val
  body_file="$(mktemp)"
  http_code_val="$(curl -sS -o "$body_file" -w '%{http_code}' -X POST "$API_URL/api/v1/digitization-batches" \
    -H "Authorization: Bearer $token" \
    -F "file=@${pdf};type=application/pdf" \
    -F "coverSheetCode=$code")"
  cat "$body_file"
  rm -f "$body_file"
  printf '\n__HTTP_STATUS__:%s' "$http_code_val"
}

generate_cover_sheets() {
  local token="$1"
  local body="$2"
  json_post "$API_URL/api/v1/cover-sheets/generate" "$body" "$token"
}

test_authentication() {
  section "0. Authentication"

  local intake_json admin_json entry_json
  intake_json="$(login intake1)"
  admin_json="$(login admin1)"
  entry_json="$(login entry1)"

  INTAKE_TOKEN="$(extract_data_field "$intake_json" token)"
  ADMIN_TOKEN="$(extract_data_field "$admin_json" token)"
  ENTRY_TOKEN="$(extract_data_field "$entry_json" token)"

  if [[ -z "$INTAKE_TOKEN" ]]; then
    log "ERROR: intake1 login failed."
    log "Response: ${intake_json:-<empty>}"
    exit 1
  fi

  if [[ -n "$INTAKE_TOKEN" ]]; then
    pass "intake1 login returns JWT"
  else
    fail "intake1 login returns JWT"
  fi

  if [[ -n "$ADMIN_TOKEN" ]]; then
    pass "admin1 login returns JWT"
  else
    fail "admin1 login returns JWT"
  fi

  if [[ -n "$ENTRY_TOKEN" ]]; then
    pass "entry1 login returns JWT"
  else
    fail "entry1 login returns JWT"
  fi

  resolve_directory_ids
}

test_database_schema() {
  section "1. Database — cover_sheets table"

  if ! psql_available; then
    log "  SKIP: PostgreSQL checks (set VIGILCARE_SKIP_DB_CHECKS=0 and start postgres)"
    return
  fi

  local table_exists index_code index_unused
  table_exists="$(psql_query "
    SELECT count(*)
    FROM information_schema.tables
    WHERE table_schema = 'public' AND table_name = 'cover_sheets';
  ")"
  index_code="$(psql_query "
    SELECT count(*)
    FROM pg_indexes
    WHERE tablename = 'cover_sheets' AND indexname = 'ix_cover_sheets_code';
  ")"
  index_unused="$(psql_query "
    SELECT count(*)
    FROM pg_indexes
    WHERE tablename = 'cover_sheets' AND indexname = 'ix_cover_sheets_unused';
  ")"

  if [[ "$table_exists" == "1" ]]; then
    pass "cover_sheets table exists"
  else
    fail "cover_sheets table exists"
  fi

  if [[ "$index_code" == "1" ]]; then
    pass "unique index ix_cover_sheets_code exists"
  else
    fail "unique index ix_cover_sheets_code exists"
  fi

  if [[ "$index_unused" == "1" ]]; then
    pass "filtered index ix_cover_sheets_unused exists"
  else
    fail "filtered index ix_cover_sheets_unused exists"
  fi
}

test_cover_sheet_generation() {
  section "2. Cover sheet generation (plan §1)"

  local gen_json count unique_count bad_format
  gen_json="$(generate_cover_sheets "$INTAKE_TOKEN" \
    '{"count":5,"batchType":"VITALS_SHEET","track":"BACKFILL"}')"

  if [[ "$(jq -er '.success' <<<"$gen_json")" == "true" ]]; then
    pass "POST /cover-sheets/generate returns success for intake1"
  else
    fail "POST /cover-sheets/generate returns success for intake1"
    log "    response: $gen_json"
    return
  fi

  count="$(jq '.data | length' <<<"$gen_json")"
  unique_count="$(jq '[.data[].code] | unique | length' <<<"$gen_json")"
  if [[ "$count" == "5" && "$unique_count" == "5" ]]; then
    pass "generate creates 5 cover sheets with unique codes"
  else
    fail "generate creates 5 cover sheets with unique codes (count=$count unique=$unique_count)"
  fi

  bad_format="$(jq -r '[.data[].code | test("^VCR-CS-[0-9A-F]{8}$")] | all' <<<"$gen_json")"
  if [[ "$bad_format" == "true" ]]; then
    pass "all codes match VCR-CS-{8-hex} format"
  else
    fail "all codes match VCR-CS-{8-hex} format"
  fi

  UPLOAD_CODE="$(jq -r '.data[0].code' <<<"$gen_json")"
  UPLOAD_COVER_SHEET_ID="$(jq -r '.data[0].id' <<<"$gen_json")"
  PDF_SHEET_IDS=($(jq -r '.data[0].id, .data[1].id' <<<"$gen_json"))

  local entry_json entry_token entry_code
  entry_json="$(login entry1)"
  entry_token="$(extract_data_field "$entry_json" token)"
  entry_code="$(http_code -X POST "$API_URL/api/v1/cover-sheets/generate" \
    -H "Authorization: Bearer $entry_token" \
    -H 'Content-Type: application/json' \
    -d '{"count":1,"batchType":"VITALS_SHEET","track":"BACKFILL"}')"
  if [[ "$entry_code" == "403" ]]; then
    pass "generate denied for DATA_ENTRY_CLERK (403)"
  else
    fail "generate denied for DATA_ENTRY_CLERK (403) — got HTTP $entry_code"
  fi

  local admin_json
  admin_json="$(generate_cover_sheets "$ADMIN_TOKEN" \
    '{"count":1,"batchType":"LAB_RESULTS","track":"BACKFILL"}')"
  if [[ "$(jq -er '.success' <<<"$admin_json")" == "true" ]]; then
    pass "generate allowed for ADMINISTRATOR"
  else
    fail "generate allowed for ADMINISTRATOR"
  fi
}

test_cover_sheet_lookup_and_list() {
  section "3. Cover sheet lookup and list (plan §2)"

  if [[ -z "$UPLOAD_CODE" ]]; then
    fail "lookup requires generated cover sheet code"
    return
  fi

  local lookup_json is_used batch_type
  lookup_json="$(json_get "$API_URL/api/v1/cover-sheets/lookup/$UPLOAD_CODE" "$INTAKE_TOKEN")"

  if [[ "$(jq -er '.success' <<<"$lookup_json")" == "true" ]]; then
    pass "GET /cover-sheets/lookup/{code} returns success"
  else
    fail "GET /cover-sheets/lookup/{code} returns success"
    return
  fi

  is_used="$(extract_bool_field "$lookup_json" isUsed)"
  batch_type="$(extract_data_field "$lookup_json" batchType)"
  if [[ "$is_used" == "false" ]]; then
    pass "lookup shows isUsed=false before upload"
  else
    fail "lookup shows isUsed=false before upload (isUsed=$is_used)"
  fi

  if [[ "$batch_type" == "VITALS_SHEET" ]]; then
    pass "lookup returns batchType VITALS_SHEET"
  else
    fail "lookup returns batchType VITALS_SHEET (got $batch_type)"
  fi

  local unknown_json unknown_code
  unknown_json="$(json_get "$API_URL/api/v1/cover-sheets/lookup/VCR-CS-DEADBEEF" "$INTAKE_TOKEN")"
  unknown_code="$(extract_error_code "$unknown_json")"
  if [[ "$unknown_code" == "COVER_SHEET_NOT_FOUND" ]]; then
    pass "lookup unknown code returns COVER_SHEET_NOT_FOUND"
  else
    fail "lookup unknown code returns COVER_SHEET_NOT_FOUND (got ${unknown_code:-<none>})"
  fi

  local list_json unused_count
  list_json="$(json_get "$API_URL/api/v1/cover-sheets?isUsed=false&page=1&pageSize=20" "$INTAKE_TOKEN")"
  unused_count="$(jq '.data | length' <<<"$list_json")"
  if [[ "$(jq -er '.success' <<<"$list_json")" == "true" && "$unused_count" -ge 5 ]]; then
    pass "GET /cover-sheets?isUsed=false lists unused sheets"
  else
    fail "GET /cover-sheets?isUsed=false lists unused sheets (count=$unused_count)"
  fi
}

test_pdf_generation() {
  section "4. Cover sheet PDF generation (plan Step 5)"

  if [[ -z "$UPLOAD_COVER_SHEET_ID" || ${#PDF_SHEET_IDS[@]} -lt 2 ]]; then
    fail "PDF tests require generated cover sheet IDs"
    return
  fi

  local pdf_tmp headers_file pdf_code content_type pdf_header
  pdf_tmp="$(mktemp)"
  headers_file="$(mktemp)"
  pdf_code="$(curl -sS -D "$headers_file" -o "$pdf_tmp" -w '%{http_code}' -X POST \
    "$API_URL/api/v1/cover-sheets/${UPLOAD_COVER_SHEET_ID}/pdf" \
    -H "Authorization: Bearer $INTAKE_TOKEN")"
  content_type="$(awk -F': ' 'tolower($1)=="content-type"{print $2}' "$headers_file" | tr -d '\r' | head -1)"
  rm -f "$headers_file"

  if [[ "$pdf_code" == "200" ]]; then
    pass "POST /cover-sheets/{id}/pdf returns 200"
  else
    fail "POST /cover-sheets/{id}/pdf returns 200 (got HTTP $pdf_code)"
  fi

  if [[ "$content_type" == application/pdf* ]]; then
    pass "single PDF response Content-Type is application/pdf"
  else
    fail "single PDF response Content-Type is application/pdf (got ${content_type:-<none>})"
  fi

  pdf_header="$(head -c 8 "$pdf_tmp" || true)"
  if [[ "$pdf_header" == %PDF-1.* ]]; then
    pass "single PDF body starts with %PDF header"
  else
    fail "single PDF body starts with %PDF header"
  fi

  if grep -aq "$UPLOAD_CODE" "$pdf_tmp" 2>/dev/null; then
    pass "single PDF embeds cover sheet code text"
  else
    fail "single PDF embeds cover sheet code text"
  fi
  rm -f "$pdf_tmp"

  local batch_pdf_tmp batch_pdf_code
  batch_pdf_tmp="$(mktemp)"
  batch_pdf_code="$(curl -sS -o "$batch_pdf_tmp" -w '%{http_code}' -X POST \
    "$API_URL/api/v1/cover-sheets/batch-pdf" \
    -H "Authorization: Bearer $INTAKE_TOKEN" \
    -H 'Content-Type: application/json' \
    -d "{\"coverSheetIds\":[\"${PDF_SHEET_IDS[0]}\",\"${PDF_SHEET_IDS[1]}\"]}")"

  if [[ "$batch_pdf_code" == "200" ]]; then
    pass "POST /cover-sheets/batch-pdf returns 200"
  else
    fail "POST /cover-sheets/batch-pdf returns 200 (got HTTP $batch_pdf_code)"
  fi

  if grep -aq '/Count 2' "$batch_pdf_tmp" 2>/dev/null || strings "$batch_pdf_tmp" | grep -q '/Count 2'; then
    pass "batch PDF contains two pages (/Count 2)"
  else
    fail "batch PDF contains two pages (/Count 2)"
  fi
  rm -f "$batch_pdf_tmp"
}

test_barcode_assisted_upload() {
  section "5. Barcode-assisted upload (plan §3)"

  if [[ -z "$UPLOAD_CODE" ]]; then
    fail "barcode upload requires generated cover sheet code"
    return
  fi

  local pdf upload_json batch_type track status entered_by
  pdf="$(unique_pdf_path upload)"
  upload_json="$(upload_with_cover_sheet "$INTAKE_TOKEN" "$UPLOAD_CODE" "$pdf")"
  rm -f "$pdf"

  if [[ "$(jq -er '.success' <<<"$upload_json")" == "true" ]]; then
    pass "POST /digitization-batches with coverSheetCode creates batch"
  else
    fail "POST /digitization-batches with coverSheetCode creates batch"
    log "    response: $upload_json"
    return
  fi

  batch_type="$(extract_data_field "$upload_json" batchType)"
  track="$(extract_data_field "$upload_json" track)"
  status="$(extract_data_field "$upload_json" status)"
  UPLOAD_BATCH_ID="$(extract_data_field "$upload_json" id)"

  if [[ "$batch_type" == "VITALS_SHEET" && "$track" == "BACKFILL" ]]; then
    pass "batch inherits batchType and track from cover sheet"
  else
    fail "batch inherits batchType and track from cover sheet (type=$batch_type track=$track)"
  fi

  local redeemed_json redeemed_used redeemed_batch_id
  redeemed_json="$(json_get "$API_URL/api/v1/cover-sheets/lookup/$UPLOAD_CODE" "$INTAKE_TOKEN")"
  redeemed_used="$(extract_bool_field "$redeemed_json" isUsed)"
  redeemed_batch_id="$(extract_data_field "$redeemed_json" batchId)"

  if [[ "$redeemed_used" == "true" && "$redeemed_batch_id" == "$UPLOAD_BATCH_ID" ]]; then
    pass "cover sheet redeemed and linked to batch after upload"
  else
    fail "cover sheet redeemed and linked to batch after upload"
  fi

  if psql_available && [[ -n "$UPLOAD_COVER_SHEET_ID" ]]; then
    local db_used db_batch
    db_used="$(psql_query "
      SELECT is_used FROM cover_sheets WHERE id = '$UPLOAD_COVER_SHEET_ID';
    ")"
    db_batch="$(psql_query "
      SELECT batch_id FROM cover_sheets WHERE id = '$UPLOAD_COVER_SHEET_ID';
    ")"
    if [[ "$db_used" == "t" && "$db_batch" == "$UPLOAD_BATCH_ID" ]]; then
      pass "database row shows is_used=true with batch_id"
    else
      fail "database row shows is_used=true with batch_id"
    fi
  fi
}

test_cover_sheet_reuse_and_unknown() {
  section "6. Reuse prevention and unknown code (plan §4)"

  if [[ -z "$UPLOAD_CODE" ]]; then
    fail "reuse test requires uploaded cover sheet code"
    return
  fi

  local pdf reuse_response reuse_status reuse_code unknown_response unknown_status unknown_code
  pdf="$(unique_pdf_path reuse)"
  reuse_response="$(upload_with_cover_sheet_status "$INTAKE_TOKEN" "$UPLOAD_CODE" "$pdf")"
  rm -f "$pdf"

  reuse_status="${reuse_response##*__HTTP_STATUS__:}"
  reuse_response="${reuse_response%$'\n'__HTTP_STATUS__:*}"
  reuse_code="$(extract_error_code "$reuse_response")"

  if [[ "$reuse_status" == "409" && "$reuse_code" == "COVER_SHEET_ALREADY_USED" ]]; then
    pass "reused cover sheet code returns 409 COVER_SHEET_ALREADY_USED"
  else
    fail "reused cover sheet code returns 409 COVER_SHEET_ALREADY_USED (HTTP $reuse_status code=${reuse_code:-<none>})"
  fi

  pdf="$(unique_pdf_path unknown)"
  unknown_response="$(upload_with_cover_sheet_status "$INTAKE_TOKEN" "VCR-CS-DEADBEEF" "$pdf")"
  rm -f "$pdf"

  unknown_status="${unknown_response##*__HTTP_STATUS__:}"
  unknown_response="${unknown_response%$'\n'__HTTP_STATUS__:*}"
  unknown_code="$(extract_error_code "$unknown_response")"

  if [[ "$unknown_status" == "404" && "$unknown_code" == "COVER_SHEET_NOT_FOUND" ]]; then
    pass "unknown cover sheet code on upload returns 404 COVER_SHEET_NOT_FOUND"
  else
    fail "unknown cover sheet code on upload returns 404 COVER_SHEET_NOT_FOUND (HTTP $unknown_status code=${unknown_code:-<none>})"
  fi
}

test_auto_assign_cover_sheet() {
  section "7. Pre-assigned cover sheet auto-assigns batch"

  if [[ -z "$ENTRY_CLERK1_ID" ]]; then
    fail "auto-assign test skipped — no entry clerk ID"
    return
  fi

  local gen_json assign_code pdf upload_json status entered_by
  gen_json="$(generate_cover_sheets "$INTAKE_TOKEN" \
    "{\"count\":1,\"batchType\":\"LAB_RESULTS\",\"track\":\"BACKFILL\",\"assignToUserId\":\"$ENTRY_CLERK1_ID\"}")"

  if [[ "$(jq -er '.success' <<<"$gen_json")" != "true" ]]; then
    fail "generate pre-assigned cover sheet"
    return
  fi

  assign_code="$(jq -r '.data[0].code' <<<"$gen_json")"
  pdf="$(unique_pdf_path assign)"
  upload_json="$(upload_with_cover_sheet "$INTAKE_TOKEN" "$assign_code" "$pdf")"
  rm -f "$pdf"

  status="$(extract_data_field "$upload_json" status)"
  entered_by="$(extract_data_field "$upload_json" enteredByUserId)"

  if [[ "$status" == "IN_ENTRY" ]]; then
    pass "pre-assigned upload transitions batch to IN_ENTRY"
  else
    fail "pre-assigned upload transitions batch to IN_ENTRY (status=$status)"
  fi

  if [[ "$entered_by" == "$ENTRY_CLERK1_ID" ]]; then
    pass "pre-assigned upload sets enteredByUserId to entry clerk"
  else
    fail "pre-assigned upload sets enteredByUserId to entry clerk (got $entered_by)"
  fi
}

test_patient_linked_cover_sheet() {
  section "8. Patient-linked cover sheet populates batch patient"

  if [[ -z "$PATIENT1_ID" ]]; then
    fail "patient-linked test skipped — no patient ID"
    return
  fi

  local gen_json patient_code pdf upload_json patient_id
  gen_json="$(generate_cover_sheets "$INTAKE_TOKEN" \
    "{\"count\":1,\"batchType\":\"MEDICATION_LIST\",\"track\":\"BACKFILL\",\"patientId\":\"$PATIENT1_ID\"}")"

  if [[ "$(jq -er '.success' <<<"$gen_json")" != "true" ]]; then
    fail "generate patient-linked cover sheet"
    return
  fi

  patient_code="$(jq -r '.data[0].code' <<<"$gen_json")"
  pdf="$(unique_pdf_path patient)"
  upload_json="$(upload_with_cover_sheet "$INTAKE_TOKEN" "$patient_code" "$pdf")"
  rm -f "$pdf"

  patient_id="$(extract_data_field "$upload_json" patientId)"
  if [[ "$patient_id" == "$PATIENT1_ID" ]]; then
    pass "patient-linked cover sheet sets batch patientId"
  else
    fail "patient-linked cover sheet sets batch patientId (got $patient_id)"
  fi
}

test_integration_tests() {
  section "9. dotnet integration tests — CoverSheetBatchTests, CoverSheetPdfTests"

  if [[ "$SKIP_TEST_CHECKS" == "1" ]]; then
    log "  SKIP: dotnet integration tests (VIGILCARE_SKIP_TEST_CHECKS=1)"
    return
  fi

  if ! command -v dotnet >/dev/null 2>&1; then
    log "  SKIP: dotnet not installed"
    return
  fi

  if dotnet test "$REPO_ROOT/VigilCareRecordsAPI.Tests/VigilCareRecordsAPI.Tests.csproj" \
    --filter "FullyQualifiedName~CoverSheet" \
    --no-restore >/tmp/vigilcare-p10-tests.log 2>&1; then
    pass "CoverSheet integration tests passed"
  else
    fail "CoverSheet integration tests passed"
    log "    see /tmp/vigilcare-p10-tests.log"
  fi
}

print_manual_ui_checklist() {
  section "10. Manual Vue UI checks (plan §5)"
  log "  Login as intake1 → /cover-sheets"
  log "    - Generate 5 VITALS_SHEET covers"
  log "    - Print Cover Sheets opens PDF in new tab"
  log "    - Cover sheet list shows unused sheets"
  log "  Navigate to /intake"
  log "    - Scan/type a cover sheet code (Enter triggers lookup)"
  log "    - Lookup auto-fills batch type, track, patient"
  log "    - Upload with Cover Sheet marks sheet as Used with linked batch ID"
}

main() {
  require_cmd curl
  require_cmd jq
  ensure_fixture_pdf

  log "VigilCare Records — Phase 10 verification"
  log "API: $API_URL"

  assert_api_reachable

  test_authentication
  test_database_schema
  test_cover_sheet_generation
  test_cover_sheet_lookup_and_list
  test_pdf_generation
  test_barcode_assisted_upload
  test_cover_sheet_reuse_and_unknown
  test_auto_assign_cover_sheet
  test_patient_linked_cover_sheet
  test_integration_tests
  print_manual_ui_checklist

  log ""
  log "Results: $PASS_COUNT passed, $FAIL_COUNT failed"
  if (( FAIL_COUNT > 0 )); then
    log "Failed checks:"
    for item in "${FAILED_TESTS[@]}"; do
      log "  - $item"
    done
    exit 1
  fi

  log "All Phase 10 API verification checks passed."
  log "Complete the manual Vue UI checklist above if not already done."
}

main "$@"
