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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

BASE_URL="${1:-${BASE_URL:-http://127.0.0.1:8000}}"
API_BASE="$BASE_URL/api"
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-20}"
RUN_QUEUE_ON_COMPLETE="${RUN_QUEUE_ON_COMPLETE:-1}"

: "${CLIENT_LOGIN:=client@lineup.fr}"
: "${CLIENT_PASSWORD:=Lineup12j}"
: "${LINER1_LOGIN:=liner@lineup.fr}"
: "${LINER1_PASSWORD:=Lineup12j}"
: "${LINER2_LOGIN:=liner2@lineup.fr}"
: "${LINER2_PASSWORD:=Lineup12j}"
: "${MISSION_LOCATION:=Gare de Cormeilles-en-Parisis, 95240 Cormeilles-en-Parisis}"
: "${MISSION_START_AT:=2026-03-13T10:00:00Z}"
: "${MISSION_BUDGET:=65}"
: "${MISSION_WAIT_MINUTES:=45}"

TMP_DIR="$(mktemp -d /tmp/lineup_e2e.XXXXXX)"
trap 'rm -rf "$TMP_DIR"' EXIT

pass_count=0
fail_count=0
MISSION_ID=""

log_info() {
  printf '%s\n' "${1:-}"
}

log_pass() {
  pass_count=$((pass_count + 1))
  printf 'PASS %s\n' "$1"
}

log_fail() {
  fail_count=$((fail_count + 1))
  printf 'FAIL %s\n' "$1"
}

json_path() {
  local path="${1:-}"
  php -r '$d=json_decode(stream_get_contents(STDIN), true); $path=$argv[1]??""; $parts=$path===""?[]:explode(".",$path); foreach($parts as $p){ if(is_array($d) && array_key_exists($p,$d)){$d=$d[$p];} else { fwrite(STDERR, "missing:$path\n"); exit(2);} } if (is_array($d)) { echo json_encode($d, JSON_UNESCAPED_SLASHES); } else { echo $d; }' "$path"
}

curl_json() {
  local method="$1"
  local url="$2"
  local body="${3:-}"
  local token="${4:-}"
  local out_file="$5"
  local status_file="$6"
  local -a headers
  headers=(-H "Accept: application/json")
  if [[ -n "$token" ]]; then
    headers+=(-H "Authorization: Bearer $token")
  fi
  if [[ -n "$body" ]]; then
    headers+=(-H "Content-Type: application/json")
    curl -sS --max-time "$TIMEOUT_SECONDS" -X "$method" "$url" \
      "${headers[@]}" \
      -d "$body" \
      -o "$out_file" \
      -w '%{http_code}' >"$status_file"
  else
    curl -sS --max-time "$TIMEOUT_SECONDS" -X "$method" "$url" \
      "${headers[@]}" \
      -o "$out_file" \
      -w '%{http_code}' >"$status_file"
  fi
}

assert_status() {
  local label="$1"
  local expected="$2"
  local actual="$3"
  if [[ "$actual" == "$expected" ]]; then
    log_pass "$label (status=$actual)"
  else
    log_fail "$label (expected=$expected, got=$actual)"
  fi
}

login() {
  local login="$1"
  local password="$2"
  local prefix="$3"
  curl_json POST "$API_BASE/auth/login" "{\"login\":\"$login\",\"password\":\"$password\"}" "" "$TMP_DIR/$prefix.body" "$TMP_DIR/$prefix.status"
  local status
  status="$(cat "$TMP_DIR/$prefix.status")"
  if [[ "$status" != "200" ]]; then
    echo "Login failed for $login" >&2
    cat "$TMP_DIR/$prefix.body" >&2
    exit 1
  fi
  log_pass "login $login"
}

extract_payin_id() {
  php -r '$d=json_decode(stream_get_contents(STDIN), true); $items=$d["data"]??[]; foreach($items as $item){ if(($item["type"]??null)==="pay_in" && !empty($item["is_default"])){ echo $item["id"]; exit(0);} } foreach($items as $item){ if(($item["type"]??null)==="pay_in"){ echo $item["id"]; exit(0);} } exit(2);'
}

count_applications_by_status() {
  local status_name="$1"
  php -r '$target=$argv[1]; $d=json_decode(stream_get_contents(STDIN), true)["data"] ?? []; $count=0; foreach($d as $item){ if(($item["status"]??null)===$target){$count++;} } echo $count;' "$status_name"
}

mission_in_paginated_data() {
  local mission_id="$1"
  php -r '$target=(int)$argv[1]; $decoded=json_decode(stream_get_contents(STDIN), true); $items=$decoded["data"]["data"] ?? $decoded["data"] ?? []; foreach($items as $item){ if((int)($item["id"] ?? 0)===$target){ echo "1"; exit(0);} } echo "0";' "$mission_id"
}

process_local_queue_once() {
  if [[ "$RUN_QUEUE_ON_COMPLETE" != "1" ]]; then
    return 0
  fi
  case "$BASE_URL" in
    http://127.0.0.1:*|http://localhost:*|http://127.0.0.1|http://localhost)
      APP_ENV="${APP_ENV:-local}" \
      DB_CONNECTION="${DB_CONNECTION:-sqlite}" \
      DB_DATABASE="${DB_DATABASE:-$ROOT_DIR/database/database.sqlite}" \
      php artisan queue:work --once --stop-when-empty >/tmp/lineup_e2e_queue.log 2>&1 || {
        echo "Queue processing failed" >&2
        cat /tmp/lineup_e2e_queue.log >&2
        return 1
      }
      log_pass "queue settlement processed locally"
      ;;
    *)
      log_info "INFO queue settlement skipped for non-local base URL"
      ;;
  esac
}

status_step() {
  local api_status="$1"
  local label="$2"
  curl_json POST "$API_BASE/missions/$MISSION_ID/status" "{\"status\":\"$api_status\"}" "$LINER1_TOKEN" "$TMP_DIR/$api_status.body" "$TMP_DIR/$api_status.status"
  local status
  status="$(cat "$TMP_DIR/$api_status.status")"
  assert_status "$label" "200" "$status"
  printf '%s' "$label" >>/dev/null
}

log_info "== LineUp local E2E =="
log_info "Base URL: $BASE_URL"
log_info "Root: $ROOT_DIR"
log_info

curl_json GET "$BASE_URL/health" "" "" "$TMP_DIR/health.body" "$TMP_DIR/health.status"
assert_status "GET /health" "200" "$(cat "$TMP_DIR/health.status")"

login "$CLIENT_LOGIN" "$CLIENT_PASSWORD" client
login "$LINER1_LOGIN" "$LINER1_PASSWORD" liner1
login "$LINER2_LOGIN" "$LINER2_PASSWORD" liner2

CLIENT_TOKEN="$(cat "$TMP_DIR/client.body" | json_path token)"
LINER1_TOKEN="$(cat "$TMP_DIR/liner1.body" | json_path token)"
LINER2_TOKEN="$(cat "$TMP_DIR/liner2.body" | json_path token)"
LINER1_ID="$(cat "$TMP_DIR/liner1.body" | json_path user.id)"
LINER2_ID="$(cat "$TMP_DIR/liner2.body" | json_path user.id)"

curl_json GET "$API_BASE/payment-methods" "" "$CLIENT_TOKEN" "$TMP_DIR/payment_methods.body" "$TMP_DIR/payment_methods.status"
assert_status "GET /api/payment-methods" "200" "$(cat "$TMP_DIR/payment_methods.status")"
PAYIN_ID="$(cat "$TMP_DIR/payment_methods.body" | extract_payin_id)"
log_pass "client pay_in method resolved (id=$PAYIN_ID)"

STAMP="$(date +%s)"
CREATE_BODY=$(cat <<JSON
{"title":"Mission E2E $STAMP","location":"$MISSION_LOCATION","expected_start_at":"$MISSION_START_AT","budget_amount":$MISSION_BUDGET,"description":"Recette E2E locale multi-candidature","event_name":"Test local","estimated_wait_minutes":$MISSION_WAIT_MINUTES}
JSON
)
curl_json POST "$API_BASE/missions" "$CREATE_BODY" "$CLIENT_TOKEN" "$TMP_DIR/create.body" "$TMP_DIR/create.status"
assert_status "POST /api/missions" "201" "$(cat "$TMP_DIR/create.status")"
MISSION_ID="$(cat "$TMP_DIR/create.body" | json_path data.id)"
MISSION_STATUS="$(cat "$TMP_DIR/create.body" | json_path data.status)"
if [[ "$MISSION_STATUS" == "published" ]]; then
  log_pass "mission created in published state (id=$MISSION_ID)"
else
  log_fail "mission created in published state (got=$MISSION_STATUS)"
fi

curl_json POST "$API_BASE/missions/$MISSION_ID/accept" "" "$LINER1_TOKEN" "$TMP_DIR/accept1.body" "$TMP_DIR/accept1.status"
assert_status "liner1 apply" "200" "$(cat "$TMP_DIR/accept1.status")"
curl_json POST "$API_BASE/missions/$MISSION_ID/accept" "" "$LINER2_TOKEN" "$TMP_DIR/accept2.body" "$TMP_DIR/accept2.status"
assert_status "liner2 apply" "200" "$(cat "$TMP_DIR/accept2.status")"

curl_json GET "$API_BASE/missions/$MISSION_ID/applications" "" "$CLIENT_TOKEN" "$TMP_DIR/apps_before.body" "$TMP_DIR/apps_before.status"
assert_status "GET /applications before choose" "200" "$(cat "$TMP_DIR/apps_before.status")"
PENDING_COUNT="$(cat "$TMP_DIR/apps_before.body" | count_applications_by_status pending)"
if [[ "$PENDING_COUNT" == "2" ]]; then
  log_pass "two pending applications before choose"
else
  log_fail "two pending applications before choose (got=$PENDING_COUNT)"
fi

curl_json POST "$API_BASE/missions/$MISSION_ID/choose-liner" "{\"liner_id\":$LINER1_ID}" "$CLIENT_TOKEN" "$TMP_DIR/choose.body" "$TMP_DIR/choose.status"
assert_status "POST /choose-liner" "200" "$(cat "$TMP_DIR/choose.status")"
if [[ "$(cat "$TMP_DIR/choose.body" | json_path data.status)" == "accepted" ]]; then
  log_pass "chosen liner accepted"
else
  log_fail "chosen liner accepted"
fi

curl_json GET "$API_BASE/missions/$MISSION_ID/applications" "" "$CLIENT_TOKEN" "$TMP_DIR/apps_after.body" "$TMP_DIR/apps_after.status"
assert_status "GET /applications after choose" "200" "$(cat "$TMP_DIR/apps_after.status")"
ACCEPTED_COUNT="$(cat "$TMP_DIR/apps_after.body" | count_applications_by_status accepted)"
REJECTED_COUNT="$(cat "$TMP_DIR/apps_after.body" | count_applications_by_status rejected)"
if [[ "$ACCEPTED_COUNT" == "1" && "$REJECTED_COUNT" == "1" ]]; then
  log_pass "applications normalized after choose"
else
  log_fail "applications normalized after choose (accepted=$ACCEPTED_COUNT rejected=$REJECTED_COUNT)"
fi

curl_json POST "$API_BASE/missions/$MISSION_ID/confirm-liner" "{\"payment_method_id\":$PAYIN_ID}" "$CLIENT_TOKEN" "$TMP_DIR/confirm.body" "$TMP_DIR/confirm.status"
CONFIRM_STATUS="$(cat "$TMP_DIR/confirm.status")"
if [[ "$CONFIRM_STATUS" == "200" || "$CONFIRM_STATUS" == "202" ]]; then
  log_pass "POST /confirm-liner (status=$CONFIRM_STATUS)"
else
  log_fail "POST /confirm-liner (expected=200/202 got=$CONFIRM_STATUS)"
fi
PAYMENT_STATUS="$(cat "$TMP_DIR/confirm.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["data"]["payment"]["status"] ?? $d["data"]["status"] ?? "";')"
if [[ "$PAYMENT_STATUS" == "paid" || "$PAYMENT_STATUS" == "pending" ]]; then
  log_pass "payment created after confirm (status=$PAYMENT_STATUS)"
else
  log_fail "payment created after confirm (got=$PAYMENT_STATUS)"
fi

curl_json GET "$API_BASE/missions" "" "$LINER1_TOKEN" "$TMP_DIR/liner_missions.body" "$TMP_DIR/liner_missions.status"
assert_status "GET /api/missions liner" "200" "$(cat "$TMP_DIR/liner_missions.status")"
if [[ "$(cat "$TMP_DIR/liner_missions.body" | mission_in_paginated_data "$MISSION_ID")" == "1" ]]; then
  log_pass "liner missions list contains selected mission"
else
  log_fail "liner missions list contains selected mission"
fi

curl_json POST "$API_BASE/missions/$MISSION_ID/messages" '{"message":"Je suis arrive pour la recette locale."}' "$LINER1_TOKEN" "$TMP_DIR/message_send.body" "$TMP_DIR/message_send.status"
MESSAGE_SEND_STATUS="$(cat "$TMP_DIR/message_send.status")"
if [[ "$MESSAGE_SEND_STATUS" == "200" || "$MESSAGE_SEND_STATUS" == "201" ]]; then
  log_pass "POST /messages (status=$MESSAGE_SEND_STATUS)"
else
  log_fail "POST /messages (expected=200/201 got=$MESSAGE_SEND_STATUS)"
fi
curl_json GET "$API_BASE/missions/$MISSION_ID/messages" "" "$CLIENT_TOKEN" "$TMP_DIR/message_list.body" "$TMP_DIR/message_list.status"
assert_status "GET /messages client" "200" "$(cat "$TMP_DIR/message_list.status")"
LAST_MESSAGE="$(cat "$TMP_DIR/message_list.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true)["data"] ?? []; if (!$d) exit(1); $last=$d[array_key_last($d)]; echo $last["message"] ?? "";')"
if [[ "$LAST_MESSAGE" == "Je suis arrive pour la recette locale." ]]; then
  log_pass "client sees liner message"
else
  log_fail "client sees liner message"
fi

status_step EN_ROUTE "mission status -> en_route"
status_step ARRIVE "mission status -> arrived"
status_step DANS_LA_FILE "mission status -> in_queue"
status_step CLIENT_ARRIVE "mission status -> client_arrived"
status_step PLACE_TRANSMISE "mission status -> spot_transmitted"
status_step MISSION_TERMINEE "mission status -> completed"

curl_json GET "$API_BASE/missions/$MISSION_ID/track" "" "$LINER1_TOKEN" "$TMP_DIR/track_liner.body" "$TMP_DIR/track_liner.status"
assert_status "GET /track liner" "200" "$(cat "$TMP_DIR/track_liner.status")"
curl_json GET "$API_BASE/missions/$MISSION_ID/track" "" "$CLIENT_TOKEN" "$TMP_DIR/track_client.body" "$TMP_DIR/track_client.status"
assert_status "GET /track client" "200" "$(cat "$TMP_DIR/track_client.status")"
TRACK_LINER_STATUS="$(cat "$TMP_DIR/track_liner.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["data"]["mission"]["status"] ?? $d["data"]["status"] ?? "";')"
TRACK_CLIENT_STATUS="$(cat "$TMP_DIR/track_client.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["data"]["mission"]["status"] ?? $d["data"]["status"] ?? "";')"
if [[ "$TRACK_LINER_STATUS" == "completed" && "$TRACK_CLIENT_STATUS" == "completed" ]]; then
  log_pass "track synchronized on completed"
else
  log_fail "track synchronized on completed (liner=$TRACK_LINER_STATUS client=$TRACK_CLIENT_STATUS)"
fi

curl_json GET "$API_BASE/wallet" "" "$LINER1_TOKEN" "$TMP_DIR/wallet_before.body" "$TMP_DIR/wallet_before.status"
assert_status "GET /wallet before queue check" "200" "$(cat "$TMP_DIR/wallet_before.status")"
WALLET_BEFORE="$(cat "$TMP_DIR/wallet_before.body" | json_path data.balance)"

process_local_queue_once || log_fail "queue settlement processed locally"

curl_json GET "$API_BASE/wallet" "" "$LINER1_TOKEN" "$TMP_DIR/wallet_after.body" "$TMP_DIR/wallet_after.status"
assert_status "GET /wallet after queue check" "200" "$(cat "$TMP_DIR/wallet_after.status")"
WALLET_AFTER="$(cat "$TMP_DIR/wallet_after.body" | json_path data.balance)"
HISTORY_URL="$API_BASE/wallet/history"
curl_json GET "$HISTORY_URL" "" "$LINER1_TOKEN" "$TMP_DIR/wallet_history.body" "$TMP_DIR/wallet_history.status"
assert_status "GET /wallet/history" "200" "$(cat "$TMP_DIR/wallet_history.status")"
LATEST_DESC="$(cat "$TMP_DIR/wallet_history.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true); $items=$d["data"]["data"] ?? []; echo $items[0]["description"] ?? "";')"
LATEST_AMOUNT="$(cat "$TMP_DIR/wallet_history.body" | php -r '$d=json_decode(stream_get_contents(STDIN), true); $items=$d["data"]["data"] ?? []; echo $items[0]["amount"] ?? "";')"
if [[ "$WALLET_AFTER" != "$WALLET_BEFORE" ]]; then
  log_pass "wallet updated after settlement (before=$WALLET_BEFORE after=$WALLET_AFTER)"
else
  log_fail "wallet updated after settlement (before=$WALLET_BEFORE after=$WALLET_AFTER)"
fi
if [[ -n "$LATEST_DESC" && -n "$LATEST_AMOUNT" ]]; then
  log_pass "wallet history contains latest settlement ($LATEST_AMOUNT)"
else
  log_fail "wallet history contains latest settlement"
fi

log_info
log_info "Summary: $pass_count passed, $fail_count failed"
log_info "Mission ID: $MISSION_ID"
log_info "Wallet before: $WALLET_BEFORE"
log_info "Wallet after:  $WALLET_AFTER"
log_info "Latest wallet history: $LATEST_DESC"

if [[ "$fail_count" -gt 0 ]]; then
  exit 1
fi
