#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
TMP_ROOT="$(mktemp -d)"
trap 'rm -rf "$TMP_ROOT"' EXIT

PASS_COUNT=0

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

fail() {
  printf 'FAIL %s\n' "$*" >&2
  exit 1
}

assert_file_contains() {
  local file="$1"
  local pattern="$2"
  if ! grep -Fq "$pattern" "$file"; then
    fail "expected '$pattern' in $file"
  fi
}

assert_not_exists_or_empty() {
  local file="$1"
  if [ -e "$file" ] && [ -s "$file" ]; then
    fail "expected empty or missing file: $file"
  fi
}

new_fixture() {
  local name="$1"
  local dir="$TMP_ROOT/$name"
  mkdir -p "$dir/bin" "$dir/baron/config/.generated/ory/kratos" "$dir/baron/config/.generated/ory/oathkeeper" "$dir/logs" "$dir/state"
  touch "$dir/baron/Makefile"
  cat > "$dir/baron/config/.generated/ory/kratos/kratos.yml" <<'EOF'
selfservice:
    allowed_return_urls:
        - http://localhost:5000
        - []
EOF
  cat > "$dir/baron/config/.generated/ory/oathkeeper/entrypoint.sh" <<'EOF'
#!/usr/bin/env sh
exit 0
EOF
  cat > "$dir/bin/docker" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
log_file="${TEST_CMD_LOG:?}"
printf 'docker %s\n' "$*" >> "$log_file"

case "$*" in
  "compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml up -d")
    if [ "${TEST_DOCKER_UP_FAIL_ONCE:-0}" = "1" ] && [ ! -f "${TEST_STATE_DIR:?}/docker-up.failed" ]; then
      touch "${TEST_STATE_DIR}/docker-up.failed"
      echo "name conflict"
      exit 1
    fi
    if [ "${TEST_DOCKER_UP_HEALTH_FAILURE:-0}" = "1" ] && [ ! -f "${TEST_STATE_DIR:?}/docker-up.health-failed" ]; then
      touch "${TEST_STATE_DIR}/docker-up.health-failed"
      echo "dependency failed to start: container baron_backend is unhealthy"
      exit 1
    fi
    echo "compose up ok"
    ;;
  "compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down")
    echo "compose down ok"
    ;;
  "compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color")
    echo "fake compose logs"
    ;;
  "ps --format {{.Names}} {{.Status}}")
    printf 'baron_backend Up (healthy)\nory_kratos Up\n'
    ;;
  "ps -a --format {{.Names}}")
    printf 'old_baron_backend\nold_ory_kratos\n'
    ;;
  "ps --format {{.Names}}")
    printf 'baron_backend\nbaron_gateway\n'
    ;;
  "rm -f old_baron_backend old_ory_kratos")
    echo "removed stale containers"
    ;;
  *)
    ;;
esac
EOF
  cat > "$dir/bin/make" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'make %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
  cat > "$dir/bin/adb" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'adb %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
  cat > "$dir/bin/pkill" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'pkill %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
  cat > "$dir/bin/chmod" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'chmod %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
  cat > "$dir/bin/chown" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'chown %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
  cat > "$dir/check.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
state_dir="${TEST_STATE_DIR:?}"
attempt_file="$state_dir/check-attempts"
count=0
if [ -f "$attempt_file" ]; then
  count="$(cat "$attempt_file")"
fi
count=$((count + 1))
printf '%s' "$count" > "$attempt_file"

mode="${CHECK_MODE:-pass}"
case "$mode" in
  pass)
    echo "RESULT ready-ish: 0 failure(s), 0 warning(s)"
    ;;
  warn_then_pass)
    if [ "$count" -eq 1 ]; then
      echo "RESULT ready-ish: 0 failure(s), 1 warning(s)"
    else
      echo "RESULT ready-ish: 0 failure(s), 0 warning(s)"
    fi
    ;;
  fail)
    echo "RESULT blocked: 1 failure(s), 0 warning(s)"
    exit 1
    ;;
  *)
    echo "unknown CHECK_MODE=$mode" >&2
    exit 2
    ;;
esac
EOF
  cat > "$dir/api-smoke.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
state_dir="${TEST_STATE_DIR:?}"
attempt_file="$state_dir/api-attempts"
count=0
if [ -f "$attempt_file" ]; then
  count="$(cat "$attempt_file")"
fi
count=$((count + 1))
printf '%s' "$count" > "$attempt_file"

mode="${API_MODE:-pass}"
case "$mode" in
  pass)
    echo "PASS api smoke"
    ;;
  login_failed)
    if [ "${TDC114_SKIP_AUTH_SMOKE:-false}" = "true" ]; then
      echo "SKIP authenticated smoke: TDC114_SKIP_AUTH_SMOKE=true"
      exit 0
    fi
    echo 'FAIL phone-login success: HTTP 401' >&2
    echo 'Response body:' >&2
    echo '{"error":"Login failed","code":"login_failed"}' >&2
    exit 1
    ;;
  fail)
    echo "FAIL api smoke" >&2
    exit 1
    ;;
  *)
    echo "unknown API_MODE=$mode" >&2
    exit 2
    ;;
esac
EOF
  cat > "$dir/wsl-maintenance.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
mode="${1:-status}"
case "$mode" in
  status)
    echo "${TEST_WSL_MAINTENANCE_OUTPUT:-WSL maintenance reminder: OK}"
    ;;
  *)
    echo "unsupported mode: $mode" >&2
    exit 2
    ;;
esac
EOF
  chmod +x "$dir/bin/docker" "$dir/bin/make" "$dir/bin/adb" "$dir/bin/pkill" \
    "$dir/bin/chmod" "$dir/bin/chown" "$dir/check.sh" "$dir/api-smoke.sh" \
    "$dir/wsl-maintenance.sh" "$dir/baron/config/.generated/ory/oathkeeper/entrypoint.sh"
  printf '%s\n' "$dir"
}

run_case() {
  local name="$1"
  shift
  say "CASE $name"
  "$@"
  PASS_COUNT=$((PASS_COUNT + 1))
}

test_startup_dry_run() {
  local fixture
  fixture="$(new_fixture startup-dry-run)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_CHECK_SCRIPT="$fixture/check.sh" \
  TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
  TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
  TDC114_START_AUTH_SERVER=false \
  DOCKER_BIN="$fixture/bin/docker" \
  MAKE_BIN="$fixture/bin/make" \
  bash "$ROOT_DIR/scripts/startup.sh" --dry-run --wait=0 --skip-android-check

  assert_not_exists_or_empty "$cmd_log"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "WSL maintenance reminder: OK"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "Dry-run mode: no actions were actually performed"
}

test_startup_auto_retry_success() {
  local fixture
  fixture="$(new_fixture startup-auto-success)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  TEST_DOCKER_UP_FAIL_ONCE=1 \
  CHECK_MODE=warn_then_pass \
  API_MODE=pass \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_CHECK_SCRIPT="$fixture/check.sh" \
  TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
  TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
  TDC114_START_AUTH_SERVER=false \
  DOCKER_BIN="$fixture/bin/docker" \
  MAKE_BIN="$fixture/bin/make" \
  bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check

  assert_file_contains "$cmd_log" "docker rm -f old_baron_backend old_ory_kratos"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "check-baron-api-env.sh reported warnings on attempt 1"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "check-baron-api-env.sh passed with zero warnings"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke.sh passed"
  if grep -Fq "        - []" "$fixture/baron/config/.generated/ory/kratos/kratos.yml"; then
    fail "expected startup to sanitize invalid empty allowed_return_urls entry"
  fi
}

test_startup_auto_health_failure_tolerated() {
  local fixture
  fixture="$(new_fixture startup-auto-health-failure)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  TEST_DOCKER_UP_HEALTH_FAILURE=1 \
  CHECK_MODE=pass \
  API_MODE=pass \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_CHECK_SCRIPT="$fixture/check.sh" \
  TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
  TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
  TDC114_START_AUTH_SERVER=false \
  DOCKER_BIN="$fixture/bin/docker" \
  MAKE_BIN="$fixture/bin/make" \
  bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check

  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "compose up reported dependency or health issues; continuing to stabilization checks before declaring failure"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke.sh passed"
}

test_startup_login_failed_falls_back_to_base_smoke() {
  local fixture
  fixture="$(new_fixture startup-login-fallback)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  CHECK_MODE=pass \
  API_MODE=login_failed \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_CHECK_SCRIPT="$fixture/check.sh" \
  TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
  TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
  TDC114_START_AUTH_SERVER=false \
  DOCKER_BIN="$fixture/bin/docker" \
  MAKE_BIN="$fixture/bin/make" \
  bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check

  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke authenticated login failed; retrying in base-smoke-only mode because TDC114_REQUIRE_AUTH_SMOKE is false"
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke base-smoke-only mode passed"
}

test_startup_auto_failure() {
  local fixture
  fixture="$(new_fixture startup-auto-failure)"
  local cmd_log="$fixture/commands.log"
  set +e
  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  CHECK_MODE=fail \
  API_MODE=pass \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_CHECK_SCRIPT="$fixture/check.sh" \
  TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
  TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
  TDC114_START_AUTH_SERVER=false \
  DOCKER_BIN="$fixture/bin/docker" \
  MAKE_BIN="$fixture/bin/make" \
  bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check
  local rc=$?
  set -e

  if [ "$rc" -eq 0 ]; then
    fail "startup auto failure case should exit non-zero"
  fi
  assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "Startup failed: health checks did not pass"
}

test_wsl_maintenance_schedule() {
  local fixture
  fixture="$(new_fixture wsl-maintenance-schedule)"
  local state_dir="$fixture/state"

  TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
  TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
  TDC114_WSL_MAINTENANCE_TODAY=2026-07-20 \
  bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-not-due.txt"

  assert_file_contains "$fixture/status-not-due.txt" "WSL maintenance reminder: OK"
  assert_file_contains "$fixture/status-not-due.txt" "Next due date: 2026-07-30"

  TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
  TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
  TDC114_WSL_MAINTENANCE_TODAY=2026-07-30 \
  bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-due.txt"

  assert_file_contains "$fixture/status-due.txt" "WSL maintenance reminder: DUE"
  assert_file_contains "$fixture/status-due.txt" "./scripts/check-wsl-maintenance.sh record"

  TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
  TDC114_WSL_MAINTENANCE_TODAY=2026-07-30 \
  bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" record > "$fixture/record.txt"

  assert_file_contains "$fixture/record.txt" "Recorded WSL maintenance date: 2026-07-30"

  TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
  TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
  TDC114_WSL_MAINTENANCE_TODAY=2026-08-05 \
  bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-after-record.txt"

  assert_file_contains "$fixture/status-after-record.txt" "WSL maintenance reminder: OK"
  assert_file_contains "$fixture/status-after-record.txt" "Base date: 2026-07-30"
}

test_shutdown_dry_run() {
  local fixture
  fixture="$(new_fixture shutdown-dry-run)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_ADB_CONNECT_ADDRESS=172.21.128.1:5562 \
  DOCKER_BIN="$fixture/bin/docker" \
  ADB_BIN="$fixture/bin/adb" \
  PKILL_BIN="$fixture/bin/pkill" \
  CHMOD_BIN="$fixture/bin/chmod" \
  CHOWN_BIN="$fixture/bin/chown" \
  bash "$ROOT_DIR/scripts/shutdown.sh" --dry-run

  assert_not_exists_or_empty "$cmd_log"
  assert_file_contains "$fixture/logs/$(date +%F)/shutdown.log" "Dry-run mode: no shutdown actions were executed"
}

test_shutdown_auto_order_and_scope() {
  local fixture
  fixture="$(new_fixture shutdown-auto)"
  local cmd_log="$fixture/commands.log"

  TEST_CMD_LOG="$cmd_log" \
  TEST_STATE_DIR="$fixture/state" \
  BARON_SSO_WORKTREE="$fixture/baron" \
  TDC114_LOG_BASE="$fixture/logs" \
  TDC114_ADB_CONNECT_ADDRESS=172.21.128.1:5562 \
  DOCKER_BIN="$fixture/bin/docker" \
  ADB_BIN="$fixture/bin/adb" \
  PKILL_BIN="$fixture/bin/pkill" \
  CHMOD_BIN="$fixture/bin/chmod" \
  CHOWN_BIN="$fixture/bin/chown" \
  bash "$ROOT_DIR/scripts/shutdown.sh" --auto

  assert_file_contains "$cmd_log" "pkill -f $ROOT_DIR/scripts/integration_tests.sh"
  assert_file_contains "$cmd_log" "pkill -f $ROOT_DIR/scripts/flutter-docker.sh"
  assert_file_contains "$cmd_log" "adb disconnect 172.21.128.1:5562"
  assert_file_contains "$cmd_log" "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color"
  assert_file_contains "$cmd_log" "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down"
  assert_file_contains "$cmd_log" "docker rm -f old_baron_backend old_ory_kratos"
  assert_file_contains "$cmd_log" "chmod -R u+w config/.generated/"
  assert_file_contains "$cmd_log" "chown -R"

  local logs_line
  local down_line
  logs_line="$(grep -n "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color" "$cmd_log" | head -n1 | cut -d: -f1)"
  down_line="$(grep -n "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down" "$cmd_log" | head -n1 | cut -d: -f1)"
  if [ -z "$logs_line" ] || [ -z "$down_line" ] || [ "$logs_line" -ge "$down_line" ]; then
    fail "expected compose logs collection before compose down"
  fi
}

run_case "startup dry-run" test_startup_dry_run
run_case "startup auto retry success" test_startup_auto_retry_success
run_case "startup auto health failure tolerated" test_startup_auto_health_failure_tolerated
run_case "startup login failure falls back" test_startup_login_failed_falls_back_to_base_smoke
run_case "startup auto failure" test_startup_auto_failure
run_case "wsl maintenance schedule" test_wsl_maintenance_schedule
run_case "shutdown dry-run" test_shutdown_dry_run
run_case "shutdown auto order and scope" test_shutdown_auto_order_and_scope

say "PASS $PASS_COUNT case(s)"
