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

suite="${1:-all}"
case "$suite" in
  ingestion|inbound|all) ;;
  *) echo "Usage: $0 <ingestion|inbound|all>" >&2; exit 64 ;;
esac

repository_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
simulate="$repository_root/scripts/simulate-order.sh"
if [ -z "${ORDER_WEBHOOK_KEY:-}" ]; then
  echo "ORDER_WEBHOOK_KEY is required." >&2
  exit 1
fi
webhook_url="${N8N_WEBHOOK_URL:-${N8N_DOMAIN:+https://${N8N_DOMAIN}/webhook/orders}}"
if [ -z "$webhook_url" ]; then
  echo "Set N8N_WEBHOOK_URL or N8N_DOMAIN." >&2
  exit 1
fi

run_id="${SMOKE_RUN_ID:-$(date +%s%N)}"
passed=0

assert_json() {
  local document="$1"
  local filter="$2"
  local message="$3"
  if ! jq -e "$filter" >/dev/null <<<"$document"; then
    echo "FAIL: $message" >&2
    jq . <<<"$document" >&2
    exit 1
  fi
  passed=$((passed + 1))
  echo "ok - $message"
}

run_scenario() {
  local salt
  case "$1" in
    cairo) salt=11 ;;
    mansoura) salt=22 ;;
    riyadh) salt=33 ;;
    london) salt=44 ;;
    fraud) salt=55 ;;
    duplicate) salt=66 ;;
    review) salt=77 ;;
    out_of_stock) salt=88 ;;
  esac
  SCENARIO_RUN_ID="${run_id}-${salt}" N8N_WEBHOOK_URL="$webhook_url" "$simulate" "$1"
}

if [ "$suite" = ingestion ] || [ "$suite" = all ]; then
  cairo="$(run_scenario cairo)"
  assert_json "$cairo" '.status == "PENDING_CONFIRMATION" and .carrier == "BOSTA" and .zone == "METRO" and .manifest.provider == "BOSTA"' "Cairo routes to Bosta Metro"
  assert_json "$cairo" '(.confirmationUrl | length) > 10 and (.telegramLink | startswith("https://t.me/"))' "confirmation response includes web and Telegram links"

  if [ -n "${INTERNAL_API_BASE_URL:-}" ] && [ -n "${INTERNAL_API_TOKEN:-}" ]; then
    order_id="$(jq -r .orderId <<<"$cairo")"
    stored="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/orders/${order_id}")"
    assert_json "$stored" '.inventoryReserved == true and .status == "PENDING_CONFIRMATION"' "Cairo stock is reserved atomically"
  fi

  mansoura="$(run_scenario mansoura)"
  assert_json "$mansoura" '.carrier == "ARAMEX" and .zone == "REGIONAL" and .manifest.provider == "ARAMEX"' "Mansoura routes to Aramex Regional"

  riyadh="$(run_scenario riyadh)"
  assert_json "$riyadh" '.carrier == "DHL" and .zone == "GULF" and .manifest.provider == "DHL"' "Riyadh routes to DHL Gulf"

  london="$(run_scenario london)"
  assert_json "$london" '.carrier == "DHL" and .zone == "INTL" and .manifest.type == "CUSTOMS_DECLARATION"' "London builds a DHL customs declaration"

  fraud="$(run_scenario fraud)"
  assert_json "$fraud" '.status == "BLOCKED" and .riskLevel == "HIGH" and .riskScore >= 70' "fraud reference order is blocked"
  if [ -n "${INTERNAL_API_BASE_URL:-}" ] && [ -n "${INTERNAL_API_TOKEN:-}" ]; then
    fraud_id="$(jq -r .orderId <<<"$fraud")"
    stored_fraud="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/orders/${fraud_id}")"
    assert_json "$stored_fraud" '.inventoryReserved == false' "blocked fraud does not reserve stock"
  fi

  duplicate="$(run_scenario duplicate)"
  assert_json "$duplicate" '.second.riskScore >= (.first.riskScore + 30)' "duplicate phone adds 30 risk points"

  review="$(run_scenario review)"
  assert_json "$review" '.status == "NEEDS_REVIEW" and .riskLevel == "MEDIUM"' "medium-risk order is sent to review"

  out_of_stock="$(run_scenario out_of_stock)"
  assert_json "$out_of_stock" '.status == "NEEDS_REVIEW"' "out-of-stock order is sent to review"
  if [ -n "${INTERNAL_API_BASE_URL:-}" ] && [ -n "${INTERNAL_API_TOKEN:-}" ]; then
    oos_id="$(jq -r .orderId <<<"$out_of_stock")"
    stored_oos="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/orders/${oos_id}")"
    assert_json "$stored_oos" '.inventoryReserved == false and any(.riskReasons[]; .code == "OUT_OF_STOCK")' "out-of-stock reason is persisted without a reservation"
  fi

  idempotency_id="${run_id}-99"
  first_idempotent="$(SCENARIO_RUN_ID="$idempotency_id" N8N_WEBHOOK_URL="$webhook_url" "$simulate" cairo)"
  second_idempotent="$(SCENARIO_RUN_ID="$idempotency_id" N8N_WEBHOOK_URL="$webhook_url" "$simulate" cairo)"
  assert_json "$(jq -cn --argjson first "$first_idempotent" --argjson second "$second_idempotent" '{first:$first,second:$second}')" '.first.orderId == .second.orderId' "externalId replay returns the same order"

  bad_key_status="$(curl --silent --output /dev/null --write-out '%{http_code}' --request POST --header 'X-Api-Key: deliberately-wrong' --header 'Content-Type: application/json' --data '{}' "$webhook_url")"
  if [ "$bad_key_status" != 401 ]; then
    echo "FAIL: bad webhook key returned HTTP $bad_key_status, expected 401" >&2
    exit 1
  fi
  passed=$((passed + 1))
  echo "ok - bad webhook key returns 401"

  missing_phone_payload="$(jq -cn --arg externalId "smoke-missing-phone-${run_id}" '{externalId:$externalId,source:"smoke-test",customer:{name:"Missing Phone",email:"missing@example.com"},shipping:{address:"12 Valid Address Cairo Egypt",city:"Cairo",governorate:"Cairo",country:"EG"},items:[{sku:"SKU-PROD-001",qty:1,unitPrice:150}],currency:"EGP"}')"
  missing_file="$(mktemp)"
  missing_status="$(curl --silent --show-error --output "$missing_file" --write-out '%{http_code}' --request POST --header "X-Api-Key: ${ORDER_WEBHOOK_KEY}" --header 'Content-Type: application/json' --data "$missing_phone_payload" "$webhook_url")"
  missing_body="$(<"$missing_file")"
  unlink "$missing_file"
  if [ "$missing_status" != 422 ] || ! jq -e '.error == "VALIDATION_ERROR"' >/dev/null <<<"$missing_body"; then
    echo "FAIL: missing phone did not return the expected 422 payload" >&2
    echo "$missing_body" >&2
    exit 1
  fi
  passed=$((passed + 1))
  echo "ok - missing phone returns 422"

  if [ -n "${MAILPIT_API_URL:-}" ]; then
    messages="$(curl --fail --silent --show-error "${MAILPIT_API_URL%/}/api/v1/messages")"
    cairo_order_id="$(jq -r .orderId <<<"$cairo")"
    message_id="$(jq -r --arg orderId "$cairo_order_id" '.messages[] | select(.Subject | contains($orderId)) | .ID' <<<"$messages" | head -n 1)"
    if [ -z "$message_id" ]; then
      echo "FAIL: no Cairo confirmation email found in Mailpit" >&2
      exit 1
    fi
    message="$(curl --fail --silent --show-error "${MAILPIT_API_URL%/}/api/v1/message/${message_id}")"
    assert_json "$message" '((.Text // "") + (.HTML // "")) | contains("https://t.me/")' "confirmation email contains the Telegram deep link"
  fi

  if [ -n "${TELEGRAM_CAPTURE_URL:-}" ]; then
    captures="$(curl --fail --silent --show-error "$TELEGRAM_CAPTURE_URL")"
    assert_json "$captures" 'any(.[]; (.text // "") | contains("تم حظر الطلب"))' "admin receives the fraud alert"
    assert_json "$captures" 'any(.[]; (.text // "") | contains("يحتاج مراجعة"))' "admin receives the review alert"
    assert_json "$captures" '[.[] | select((.text // "") | contains("المخزون منخفض"))] | length == 1' "low-stock SKU triggers exactly one restock alert"
  fi
fi

if [ "$suite" = inbound ] || [ "$suite" = all ]; then
  if [ -z "${INTERNAL_API_BASE_URL:-}" ] || [ -z "${INTERNAL_API_TOKEN:-}" ]; then
    echo "INTERNAL_API_BASE_URL and INTERNAL_API_TOKEN are required for inbound smoke tests." >&2
    exit 1
  fi
  test_inbound_url="${N8N_TEST_INBOUND_URL:-${webhook_url%/orders}/test-inbound}"
  linked_order=""
  linked_order_id=""
  linked_chat_id=""

  create_linked_order() {
    local salt="$1"
    linked_chat_id="chat-${run_id}-${salt}"
    linked_order="$(SCENARIO_RUN_ID="${run_id}-${salt}" N8N_WEBHOOK_URL="$webhook_url" "$simulate" cairo)"
    linked_order_id="$(jq -r .orderId <<<"$linked_order")"
    local token
    token="$(jq -r .telegramLink <<<"$linked_order" | sed 's/.*start=//')"
    curl --fail-with-body --silent --show-error \
      --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" \
      --header "Content-Type: application/json" \
      --data "$(jq -cn --arg token "$token" --arg chatId "$linked_chat_id" '{token:$token,chatId:$chatId,username:"smoke_buyer"}')" \
      "${INTERNAL_API_BASE_URL%/}/api/internal/customers/link-telegram" >/dev/null
  }

  post_inbound() {
    curl --fail-with-body --silent --show-error \
      --header "X-Api-Key: ${ORDER_WEBHOOK_KEY}" \
      --header "Content-Type: application/json" \
      --data "$1" \
      "$test_inbound_url"
  }

  wait_for_order() {
    local order_id="$1"
    local filter="$2"
    local document=""
    for _attempt in $(seq 1 30); do
      document="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/orders/${order_id}")"
      if jq -e "$filter" >/dev/null <<<"$document"; then
        printf %s "$document"
        return 0
      fi
      sleep 1
    done
    echo "Timed out waiting for order ${order_id}: ${filter}" >&2
    jq . <<<"$document" >&2
    return 1
  }

  wait_for_customer() {
    local chat_id="$1"
    local filter="$2"
    local document=""
    for _attempt in $(seq 1 30); do
      document="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/customers/by-telegram/${chat_id}")"
      if jq -e "$filter" >/dev/null <<<"$document"; then
        printf %s "$document"
        return 0
      fi
      sleep 1
    done
    echo "Timed out waiting for customer ${chat_id}: ${filter}" >&2
    jq . <<<"$document" >&2
    return 1
  }

  wrong_hook_status="$(curl --silent --output /dev/null --write-out '%{http_code}' --header 'X-Api-Key: wrong' --header 'Content-Type: application/json' --data '{}' "$test_inbound_url")"
  if [ "$wrong_hook_status" != 404 ]; then
    echo "FAIL: unavailable test hook returned HTTP $wrong_hook_status, expected 404" >&2
    exit 1
  fi
  passed=$((passed + 1))
  echo "ok - inbound test hook requires both feature flag and key"

  create_linked_order 911
  confirm_id="$linked_order_id"
  confirm_chat="$linked_chat_id"
  confirm_payload="$(jq -cn --arg chatId "$confirm_chat" '{chatId:$chatId,text:"أيوه أكد الطلب",messageType:"VOICE",transcript:"أيوه أكد الطلب",analysis:{intent:"confirm",new_address:null,cancel_reason:null,sentiment:"positive",confidence:0.99,reply_ar:"تم تأكيد الطلب"}}')"
  assert_json "$(post_inbound "$confirm_payload")" '.accepted == true' "canned Arabic voice intent is accepted"
  confirmed="$(wait_for_order "$confirm_id" '.status == "CONFIRMED" and .awbNumber != null and .labelPdfPath != null')"
  assert_json "$confirmed" '.inventoryReserved == false and (.awbNumber | startswith("WB-"))' "voice confirmation deducts stock and generates AWB/PDF"

  create_linked_order 922
  retention_id="$linked_order_id"
  retention_chat="$linked_chat_id"
  price_payload="$(jq -cn --arg chatId "$retention_chat" '{chatId:$chatId,text:"مش عايزه غالي أوي",messageType:"TEXT",analysis:{intent:"cancel",new_address:null,cancel_reason:"price",sentiment:"negative",confidence:0.96,reply_ar:"يمكننا تقديم خصم"}}')"
  post_inbound "$price_payload" >/dev/null
  offered="$(wait_for_order "$retention_id" '.status == "PENDING_CONFIRMATION" and .retentionOffered == true and (.discount | tonumber) > 0')"
  assert_json "$offered" '.retentionOffered == true' "price objection receives the configured retention discount"
  accept_payload="$(jq -cn --arg chatId "$retention_chat" '{chatId:$chatId,text:"موافق",messageType:"TEXT",analysis:{intent:"confirm",new_address:null,cancel_reason:null,sentiment:"positive",confidence:0.99,reply_ar:"تم"}}')"
  post_inbound "$accept_payload" >/dev/null
  accepted_order="$(wait_for_order "$retention_id" '.status == "CONFIRMED"')"
  assert_json "$accepted_order" '.status == "CONFIRMED" and (.discount | tonumber) > 0' "accepting retention confirms the discounted order"

  create_linked_order 933
  decline_id="$linked_order_id"
  decline_chat="$linked_chat_id"
  decline_price="$(jq -cn --arg chatId "$decline_chat" '{chatId:$chatId,text:"غالي",messageType:"TEXT",analysis:{intent:"cancel",new_address:null,cancel_reason:"price",sentiment:"negative",confidence:0.95,reply_ar:"عرض"}}')"
  post_inbound "$decline_price" >/dev/null
  wait_for_order "$decline_id" '.retentionOffered == true' >/dev/null
  decline_callback="$(jq -cn --arg chatId "$decline_chat" --arg callbackData "ret:decline:${decline_id}" '{chatId:$chatId,callbackData:$callbackData,messageType:"BUTTON"}')"
  post_inbound "$decline_callback" >/dev/null
  declined="$(wait_for_order "$decline_id" '.status == "CANCELLED"')"
  assert_json "$declined" '.inventoryReserved == false and .cancelReason == "price"' "declining retention cancels and releases stock"

  create_linked_order 944
  angry_id="$linked_order_id"
  angry_chat="$linked_chat_id"
  angry_payload="$(jq -cn --arg chatId "$angry_chat" '{chatId:$chatId,text:"انتو نصابين!!",messageType:"TEXT",analysis:{intent:"complaint",new_address:null,cancel_reason:null,sentiment:"angry",confidence:0.99,reply_ar:"سيتم التصعيد"}}')"
  post_inbound "$angry_payload" >/dev/null
  angry_order="$(wait_for_order "$angry_id" '.status == "NEEDS_REVIEW"')"
  assert_json "$angry_order" '.status == "NEEDS_REVIEW"' "angry complaint is escalated for human review"

  create_linked_order 955
  unclear_id="$linked_order_id"
  unclear_chat="$linked_chat_id"
  unclear_payload="$(jq -cn --arg chatId "$unclear_chat" '{chatId:$chatId,text:"سشضثقفغ",messageType:"TEXT",analysis:{intent:"unclear",new_address:null,cancel_reason:null,sentiment:"neutral",confidence:0.2,reply_ar:"لم أفهم"}}')"
  post_inbound "$unclear_payload" >/dev/null
  unclear_order="$(wait_for_order "$unclear_id" '.status == "NEEDS_REVIEW"')"
  assert_json "$unclear_order" '.status == "NEEDS_REVIEW"' "low-confidence gibberish is escalated"

  create_linked_order 966
  address_id="$linked_order_id"
  address_chat="$linked_chat_id"
  address_request="$(jq -cn --arg chatId "$address_chat" '{chatId:$chatId,text:"عايز أغير العنوان",messageType:"TEXT",analysis:{intent:"change_address",new_address:null,cancel_reason:null,sentiment:"neutral",confidence:0.98,reply_ar:"أرسل العنوان"}}')"
  post_inbound "$address_request" >/dev/null
  wait_for_customer "$address_chat" '.pendingAction == "AWAIT_ADDRESS"' >/dev/null
  address_value="$(jq -cn --arg chatId "$address_chat" '{chatId:$chatId,text:"25 شارع الجيش المنصورة الدقهلية",messageType:"TEXT"}')"
  post_inbound "$address_value" >/dev/null
  updated_address="$(wait_for_order "$address_id" '.carrier == "ARAMEX" and .zone == "REGIONAL" and .city == "المنصورة"')"
  address_customer="$(wait_for_customer "$address_chat" '.pendingAction == "NONE"')"
  assert_json "$(jq -cn --argjson order "$updated_address" --argjson customer "$address_customer" '{order:$order,customer:$customer}')" '.order.governorate == "الدقهلية" and .customer.pendingAction == "NONE"' "address conversation re-parses and re-routes the order"

  create_linked_order 977
  forged_id="$linked_order_id"
  forged_chat="$linked_chat_id"
  forged_payload="$(jq -cn --arg chatId "$forged_chat" '{chatId:$chatId,callbackData:"act:confirm:ORD-1789800000000",messageType:"BUTTON"}')"
  post_inbound "$forged_payload" >/dev/null
  sleep 1
  forged_order="$(curl --fail-with-body --silent --show-error --header "Authorization: Bearer ${INTERNAL_API_TOKEN}" "${INTERNAL_API_BASE_URL%/}/api/internal/orders/${forged_id}")"
  assert_json "$forged_order" '.status == "PENDING_CONFIRMATION"' "forged callback cannot mutate another order"

  if [ -n "${TELEGRAM_CAPTURE_URL:-}" ]; then
    captures="$(curl --fail --silent --show-error "$TELEGRAM_CAPTURE_URL")"
    assert_json "$captures" 'any(.[]; (.path // "") | contains("setWebhook"))' "the single Telegram trigger registers its webhook"
    assert_json "$captures" 'any(.[]; (.text // "") | contains("جهّز الطلب"))' "confirmation alerts the warehouse"
    assert_json "$captures" 'any(.[]; (.text // "") | contains("خصم 10"))' "retention offer is delivered with buttons"
    assert_json "$captures" 'any(.[]; ((.text // "") | contains("انتو نصابين")) and .chat_id == "123456789")' "angry transcript reaches the admin group"
  fi
fi

echo "${suite^} smoke test passed (${passed} assertions)."
