#!/usr/bin/env bash
# axiUm CAPI/Hub - Full safelist connectivity test (no authentication)
# Usage: ./test-axium-outbound-connectivity.sh
# Optional env:
#   CAPI_BASE_URL=https://capi.host:3001
#   HUB_PORT_CHECK_HOST=127.0.0.1   (or Hub hostname/IP for outbound TCP test from this machine)
#   SKIP_LISTEN_CHECK=1            (skip local LISTEN check; use from a client PC that is not the Hub)
set -euo pipefail

TIMEOUT="${TIMEOUT_SEC:-15}"
OK_CODES='200 201 204 301 302 400 401 403 404 405 406 415 500 502 503'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

add() { ROWS+=("$1|$2|$3|$4"); }

ROWS=()
if [[ -n "${CAPI_BASE_URL:-}" ]]; then
  base="${CAPI_BASE_URL%/}"
  add "General CAPI (outbound)" "CAPI API (your baseUri)" "${base}/api" "https"
fi
add "General CAPI (outbound)" "Docker Hub registry" "https://registry-1.docker.io/v2/" "https"
add "General CAPI (outbound)" "Docker Hub index" "https://index.docker.io/v1/" "https"
add "General CAPI (outbound)" "Pendo track API" "https://app.pendo.io/data/track" "https"
add "General CAPI (outbound)" "Pendo app" "https://app.pendo.io/" "https"
add "General CAPI (outbound)" "Pendo CDN" "https://cdn.pendo.io/" "https"
add "GitLab (outbound)" "GitLab" "https://gitlab.com/" "https"
add "GitLab (outbound)" "GitLab Container Registry" "https://registry.gitlab.com/v2/" "https"
add "GitLab (outbound)" "GitLab Feature Flags (PROD)" "https://gitlab.com/api/v4/feature_flags/unleash/53034284" "https"
add "Redox (outbound)" "Redox query" "https://api.redoxengine.com/query" "https"
add "Redox (outbound)" "Redox authenticate" "https://api.redoxengine.com/auth/authenticate" "https"
add "Redox (outbound)" "Redox endpoint" "https://api.redoxengine.com/endpoint" "https"
add "DynamoDB (outbound)" "DynamoDB us-west-2" "https://dynamodb.us-west-2.amazonaws.com" "dynamo"
add "DynamoDB (outbound)" "DynamoDB FIPS" "https://dynamodb-fips.us-west-2.amazonaws.com" "dynamo"
add "DentalXChange / DXC (outbound)" "DXC Web Services" "https://webservices.dentalxchange.com/dws/DwsService" "https"
add "DentalXChange / DXC (outbound)" "DXC SFTP" "sftp2.dentalxchange.com:22" "tcp"
add "Ansible Galaxy (install prep)" "Ansible Galaxy API" "https://galaxy.ansible.com/api/v3/" "https"

HUB_PORT_HOST="${HUB_PORT_CHECK_HOST:-127.0.0.1}"
add "Hub ports (TCP)" "TCP inbound (LISTEN) 8443" "8443" "listen"
add "Hub ports (TCP)" "TCP inbound (LISTEN) 8080" "8080" "listen"
add "Hub ports (TCP)" "TCP outbound (connect) 8443" "${HUB_PORT_HOST}:8443" "tcp"
add "Hub ports (TCP)" "TCP outbound (connect) 8080" "${HUB_PORT_HOST}:8080" "tcp"

http_code_for() {
  local url="$1" kind="$2" code
  if [[ "$kind" == "dynamo" ]]; then
    code=$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout "$TIMEOUT" --max-time "$TIMEOUT" \
      -X POST -H 'Content-Type: application/x-amz-json-1.0' -d '{}' "$url" 2>/dev/null || true)
  else
    code=$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout "$TIMEOUT" --max-time "$TIMEOUT" \
      -L -X GET "$url" 2>/dev/null || true)
  fi
  code="${code//$'\r'/}"; code="${code:0:3}"
  [[ "$code" =~ ^[0-9]{3}$ ]] && echo "$code" || echo "000"
}

test_tcp() { timeout 5 bash -c "echo >/dev/tcp/${1}/${2}" 2>/dev/null; }

# True if something is listening on TCP port on this machine (inbound readiness)
test_listen_local() {
  local port="$1"
  if command -v ss >/dev/null 2>&1; then
    ss -tln 2>/dev/null | grep -qE ":${port}([[:space:]]|$)" && return 0
    return 1
  fi
  if command -v netstat >/dev/null 2>&1; then
    netstat -tln 2>/dev/null | grep -qE ":${port}\s" && return 0
    return 1
  fi
  return 2
}

NAME_COL=36
URL_COL=58

print_result() {
  local outcome="$1" name="$2" target="$3" detail="$4"
  if [[ "$outcome" == "PASS" ]]; then
    printf "${GREEN}%-4s${NC}  %-${NAME_COL}s  %-${URL_COL}s  (%s)\n" "PASS" "$name" "$target" "$detail"
  else
    printf "${RED}%-4s${NC}  %-${NAME_COL}s  %-${URL_COL}s  (%s)\n" "FAIL" "$name" "$target" "$detail"
  fi
}

test_row() {
  local name="$1" target="$2" kind="$3"
  local host port code

  if [[ "$kind" == "listen" ]]; then
    port="$target"
    if [[ -n "${SKIP_LISTEN_CHECK:-}" ]]; then
      print_result "PASS" "$name" "local:${port}" "skipped (SKIP_LISTEN_CHECK)"
      return 0
    fi
    test_listen_local "$port"
    code=$?
    if [[ "$code" -eq 0 ]]; then
      print_result "PASS" "$name" "local:${port}" "LISTEN"
      return 0
    fi
    if [[ "$code" -eq 2 ]]; then
      print_result "PASS" "$name" "local:${port}" "skipped (no ss/netstat)"
      return 0
    fi
    print_result "FAIL" "$name" "local:${port}" "not LISTEN"
    return 1
  fi

  if [[ "$kind" == "tcp" ]]; then
    host="${target%%:*}"; port="${target##*:}"
    if ! getent hosts "$host" >/dev/null 2>&1; then
      print_result "FAIL" "$name" "$target" "DNS"; return 1
    fi
    if test_tcp "$host" "$port"; then
      print_result "PASS" "$name" "$target" "TCP $port open"; return 0
    fi
    print_result "FAIL" "$name" "$target" "TCP $port blocked"; return 1
  fi

  host=$(echo "$target" | sed -E 's#https?://([^/:]+).*#\1#')
  if ! getent hosts "$host" >/dev/null 2>&1; then
    print_result "FAIL" "$name" "$target" "DNS"; return 1
  fi
  code=$(http_code_for "$target" "$kind")
  if [[ "$code" == "000" ]]; then
    print_result "FAIL" "$name" "$target" "no response"; return 1
  fi
  if echo " $OK_CODES " | grep -q " $code "; then
    print_result "PASS" "$name" "$target" "HTTP $code"; return 0
  fi
  print_result "FAIL" "$name" "$target" "HTTP $code"; return 1
}

print_inbound() {
  echo "--- $1 ---"
  echo "    $2"
  shift 2
  for item in "$@"; do echo "    $item"; done
  echo "    -> Configure INBOUND on firewall (not verified by this script)."
  echo ""
}

echo ""
echo "axiUm connectivity test - full safelist"
echo "Host: $(hostname -f 2>/dev/null || hostname)"
[[ -n "${CAPI_BASE_URL:-}" ]] && echo "CAPI_BASE_URL: $CAPI_BASE_URL"
echo "HUB_PORT_CHECK_HOST: $HUB_PORT_HOST (TCP connect tests)"
[[ -n "${SKIP_LISTEN_CHECK:-}" ]] && echo "SKIP_LISTEN_CHECK: set (local LISTEN checks skipped)"
echo ""

fail=0
current_section=""
for row in "${ROWS[@]}"; do
  IFS='|' read -r section name target kind <<< "$row"
  if [[ "$section" != "$current_section" ]]; then
    [[ -n "$current_section" ]] && echo ""
    echo "--- $section ---"
    current_section="$section"
  fi
  if ! test_row "$name" "$target" "$kind"; then fail=$((fail + 1)); fi
done
echo ""

print_inbound "Inbound - Engage" "Allow INCOMING to CAPI from Engage IPs." \
  "(US) Engage Test  35.237.235.73" "(US) Engage Prod  34.74.229.226" \
  "(CA) Engage Test  34.47.49.155" "(CA) Engage Prod  34.47.49.155"
print_inbound "Inbound - HS1 Support" "Allow INCOMING for axiUm Support." \
  "64.114.79.24/29" "207.194.141.232/29"
print_inbound "Inbound - Scheduling Service" "Allow INCOMING to CAPI from Scheduling Service." \
  "68.68.115.212/32" "68.68.123.132/32"

exit $fail