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

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

if [[ -f .env ]]; then
  set -a
  # shellcheck source=/dev/null
  source .env
  set +a
fi

POOL_DOMAIN="${POOL_DOMAIN:-pool.vexonus.com}"
VEX_DOCS_DOMAIN="${VEX_DOCS_DOMAIN:-docs.vexonus.com}"
POOL_DB_NAME="${POOL_DB_NAME:-vexonpool}"
STRATUM_PORT="${STRATUM_PORT:-4499}"
VEX_POOL_ROOT="${VEX_POOL_ROOT:-/opt/vexon-pool}"
VEX_BIN_DIR="${VEX_BIN_DIR:-$VEX_POOL_ROOT/bin}"
VEX_WEB_DIR="${VEX_WEB_DIR:-$VEX_POOL_ROOT/web}"
VEX_DOCS_DIR="${VEX_DOCS_DIR:-$VEX_POOL_ROOT/docs}"
VEX_LOG_DIR="${VEX_LOG_DIR:-$VEX_POOL_ROOT/log}"
VEX_STRATUM_CONFIG_DIR="${VEX_STRATUM_CONFIG_DIR:-$VEX_POOL_ROOT/stratum-config}"
VEX_RPC_PORT="${VEX_RPC_PORT:-18556}"
VEX_RPC_USER="${VEX_RPC_USER:-}"
VEX_RPC_PASSWORD="${VEX_RPC_PASSWORD:-}"
VEX_ADDRESS="${VEX_ADDRESS:-}"
VEX_MIN_PAYOUT="${VEX_MIN_PAYOUT:-0.001}"
VEX_WEB_USER="${VEX_WEB_USER:-www-data}"
VEX_WEB_GROUP="${VEX_WEB_GROUP:-www-data}"

failures=0
warnings=0

ok() { printf '[OK] %s\n' "$1"; }
warn() { printf '[WARN] %s\n' "$1"; warnings=$((warnings + 1)); }
fail() { printf '[FAIL] %s\n' "$1"; failures=$((failures + 1)); }

have() { command -v "$1" >/dev/null 2>&1; }

check_port() {
  local port="$1"
  local label="$2"
  if have ss && ss -lnt | awk '{print $4}' | grep -Eq "[:.]${port}$"; then
    ok "$label is listening on $port"
  else
    fail "$label is not listening on $port"
  fi
}

check_http() {
  local host="$1"
  local path="${2:-/}"
  if ! have curl; then
    warn "curl is not installed; skipped HTTP check for $host"
    return
  fi
  local code
  code="$(curl -k -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1${path}" -H "Host: ${host}" || true)"
  if [[ "$code" =~ ^(2|3) ]]; then
    ok "HTTP ${host}${path} returned $code"
  else
    fail "HTTP ${host}${path} returned ${code:-no response}"
  fi
}

check_rendered_page() {
  local host="$1"
  local path="$2"
  local label="$3"
  if ! have curl; then
    warn "curl is not installed; skipped rendered page check for $label"
    return
  fi

  local tmp code bytes url
  tmp="/tmp/vexon-health-page.$$.$RANDOM"

  if have ss && ss -lnt | awk '{print $4}' | grep -Eq '[:.]443$'; then
    url="https://${host}${path}"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' --resolve "${host}:443:127.0.0.1" "$url" || true)"
  else
    url="http://127.0.0.1${path}"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' -H "Host: ${host}" "$url" || true)"
  fi

  bytes="$(wc -c < "$tmp" 2>/dev/null | tr -d ' ' || echo 0)"
  if [[ "$code" =~ ^2 && "$bytes" =~ ^[0-9]+$ ]] && (( bytes > 20 )); then
    if grep -qiE 'Internal Server Error|Permission denied|Trying to access array offset|Fatal error|Parse error|failed to open stream' "$tmp"; then
      fail "$label rendered an application error"
    else
      ok "$label rendered successfully (${bytes} bytes)"
    fi
  else
    fail "$label returned ${code:-no response} with ${bytes:-0} byte(s)"
  fi

  rm -f "$tmp"
}

check_json_api() {
  local host="$1"
  local path="$2"
  local label="$3"
  if ! have curl; then
    warn "curl is not installed; skipped API check for $label"
    return
  fi

  local tmp code bytes url first
  tmp="/tmp/vexon-health-api.$$.$RANDOM"

  if have ss && ss -lnt | awk '{print $4}' | grep -Eq '[:.]443$'; then
    url="https://${host}${path}"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' --resolve "${host}:443:127.0.0.1" "$url" || true)"
  else
    url="http://127.0.0.1${path}"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' -H "Host: ${host}" "$url" || true)"
  fi

  bytes="$(wc -c < "$tmp" 2>/dev/null | tr -d ' ' || echo 0)"
  first="$(tr -d '[:space:]' < "$tmp" 2>/dev/null | head -c 1 || true)"
  if [[ "$code" =~ ^2 && "$bytes" =~ ^[0-9]+$ ]] && (( bytes > 2 )) && [[ "$first" =~ ^[\{\[]$ ]]; then
    if grep -qiE 'Internal Server Error|Permission denied|Trying to access array offset|Fatal error|Parse error|failed to open stream' "$tmp"; then
      fail "$label returned an application error"
    else
      ok "$label returned JSON successfully (${bytes} bytes)"
    fi
  else
    fail "$label returned ${code:-no response} with ${bytes:-0} byte(s)"
  fi

  rm -f "$tmp"
}

check_broadcast_api() {
  local host="$1"
  local label="$2"
  if ! have curl; then
    warn "curl is not installed; skipped API check for $label"
    return
  fi

  local tmp code bytes url first
  tmp="/tmp/vexon-health-broadcast.$$.$RANDOM"

  if have ss && ss -lnt | awk '{print $4}' | grep -Eq '[:.]443$'; then
    url="https://${host}/api/broadcast"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' \
      --resolve "${host}:443:127.0.0.1" \
      -H 'Content-Type: application/json' \
      -X POST --data '{"rawtx":"00"}' "$url" || true)"
  else
    url="http://127.0.0.1/api/broadcast"
    code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' \
      -H "Host: ${host}" \
      -H 'Content-Type: application/json' \
      -X POST --data '{"rawtx":"00"}' "$url" || true)"
  fi

  bytes="$(wc -c < "$tmp" 2>/dev/null | tr -d ' ' || echo 0)"
  first="$(tr -d '[:space:]' < "$tmp" 2>/dev/null | head -c 1 || true)"
  if [[ "$code" =~ ^2 && "$bytes" =~ ^[0-9]+$ ]] && (( bytes > 2 )) && [[ "$first" == "{" ]] && grep -q '"type":"broadcast"' "$tmp"; then
    ok "$label returned broadcast JSON validation (${bytes} bytes)"
  else
    fail "$label returned ${code:-no response} with ${bytes:-0} byte(s)"
  fi

  rm -f "$tmp"
}

check_web_writable() {
  local path="$1"
  local label="$2"

  if [[ ! -e "$path" ]]; then
    fail "$label is missing: $path"
    return
  fi

  if id "$VEX_WEB_USER" >/dev/null 2>&1 && have runuser; then
    if runuser -u "$VEX_WEB_USER" -- test -w "$path"; then
      ok "$label is writable by $VEX_WEB_USER"
    else
      fail "$label is not writable by $VEX_WEB_USER: $path"
    fi
    return
  fi

  if [[ -w "$path" ]]; then
    ok "$label is writable by current user"
  else
    fail "$label is not writable: $path"
  fi
}

echo "== Vexon Pool health check =="
date

echo
echo "== ports =="
check_port "$STRATUM_PORT" "Stratum"
check_port "$VEX_RPC_PORT" "Vexon node RPC"
if have ss; then
  stratum_connections="$(ss -tn | awk -v port=":${STRATUM_PORT}" '$0 ~ port {count++} END {print count + 0}')"
  if [[ "$stratum_connections" =~ ^[0-9]+$ ]] && (( stratum_connections > 0 )); then
    ok "active Stratum TCP connections: $stratum_connections"
  else
    warn "no active Stratum TCP connections"
  fi
fi

echo
echo "== node rpc =="
if [[ -x "$VEX_BIN_DIR/vexonctl" && -n "$VEX_RPC_USER" && -n "$VEX_RPC_PASSWORD" ]]; then
  if "$VEX_BIN_DIR/vexonctl" --notls --rpcserver "127.0.0.1:${VEX_RPC_PORT}" -u "$VEX_RPC_USER" -P "$VEX_RPC_PASSWORD" getblockcount >/tmp/vexon-health-blockcount.$$ 2>/tmp/vexon-health-rpc.err.$$; then
    ok "node RPC getblockcount: $(cat /tmp/vexon-health-blockcount.$$)"
  else
    fail "node RPC failed: $(cat /tmp/vexon-health-rpc.err.$$ 2>/dev/null)"
  fi
  rm -f /tmp/vexon-health-blockcount.$$ /tmp/vexon-health-rpc.err.$$

  peers="$("$VEX_BIN_DIR/vexonctl" --notls --rpcserver "127.0.0.1:${VEX_RPC_PORT}" -u "$VEX_RPC_USER" -P "$VEX_RPC_PASSWORD" getconnectioncount 2>/dev/null || echo 0)"
  if [[ "$peers" =~ ^[0-9]+$ ]] && (( peers > 0 )); then
    ok "node peer count: $peers"
  else
    warn "node peer count is $peers; keep local peer enabled until public seed nodes exist"
  fi

  if "$VEX_BIN_DIR/vexonctl" --notls --rpcserver "127.0.0.1:${VEX_RPC_PORT}" -u "$VEX_RPC_USER" -P "$VEX_RPC_PASSWORD" getwork >/dev/null 2>&1; then
    ok "node can create getwork templates"
  else
    fail "node cannot create getwork templates"
  fi
else
  fail "missing vexonctl or RPC credentials"
fi

echo
echo "== database =="
if have mysql; then
  if mysql -uroot "$POOL_DB_NAME" -Nse "SELECT 1" >/dev/null 2>&1; then
    ok "database is reachable: $POOL_DB_NAME"
  else
    fail "database is not reachable: $POOL_DB_NAME"
  fi

  coin="$(mysql -uroot "$POOL_DB_NAME" -Nse "SELECT CONCAT_WS('|', symbol, enable, auto_ready, algo, rpcencoding, rpcport) FROM coins WHERE symbol='VEX' LIMIT 1" 2>/dev/null || true)"
  if [[ -n "$coin" ]]; then
    IFS='|' read -r coin_symbol coin_enable coin_auto_ready coin_algo coin_rpcencoding coin_rpcport <<< "$coin"
    ok "VEX coin row: symbol=$coin_symbol enable=$coin_enable auto_ready=$coin_auto_ready algo=$coin_algo rpcencoding=$coin_rpcencoding rpcport=$coin_rpcport"
    [[ "$coin_enable" == "1" ]] || fail "VEX coin is not enabled"
    [[ "$coin_auto_ready" == "1" ]] || fail "VEX coin auto_ready is not enabled"
    [[ "$coin_algo" == "decred" ]] || fail "VEX coin algo should be Blake256r14 compatibility mode, got $coin_algo"
    [[ "$coin_rpcencoding" == "DCR" ]] || fail "VEX coin rpcencoding should be DCR, got $coin_rpcencoding"
    [[ "$coin_rpcport" == "$VEX_RPC_PORT" ]] || fail "VEX coin rpcport should be $VEX_RPC_PORT, got $coin_rpcport"
  else
    fail "missing VEX coin row"
  fi

  recent="$(mysql -uroot "$POOL_DB_NAME" -Nse "SELECT COUNT(*) FROM shares WHERE valid=1 AND algo='decred' AND time>UNIX_TIMESTAMP()-900" 2>/dev/null || echo 0)"
  if [[ "$recent" =~ ^[0-9]+$ ]] && (( recent > 0 )); then
    ok "accepted shares in last 15 minutes: $recent"
  else
    warn "no accepted shares in the last 15 minutes"
  fi

  payable="$(mysql -uroot "$POOL_DB_NAME" -Nse "SELECT COUNT(*) FROM accounts A JOIN coins C ON C.id=A.coinid WHERE C.symbol='VEX' AND A.balance >= ${VEX_MIN_PAYOUT}" 2>/dev/null || echo 0)"
  ok "accounts at or above payout minimum: ${payable:-0}"

  failed="$(mysql -uroot "$POOL_DB_NAME" -Nse "SELECT COUNT(*) FROM payouts P JOIN accounts A ON A.id=P.account_id JOIN coins C ON C.id=A.coinid WHERE C.symbol='VEX' AND P.completed=0 AND P.errmsg<>''" 2>/dev/null || echo 0)"
  if [[ "$failed" =~ ^[0-9]+$ ]] && (( failed > 0 )); then
    warn "failed payout rows: $failed"
  else
    ok "no failed payout rows"
  fi
else
  fail "mysql client is not installed"
fi

echo
echo "== stratum =="
if [[ -f "$VEX_STRATUM_CONFIG_DIR/vexon.log" ]]; then
  tail -30 "$VEX_STRATUM_CONFIG_DIR/vexon.log"
  recent_stratum_log="$(tail -120 "$VEX_STRATUM_CONFIG_DIR/vexon.log")"
  if printf '%s\n' "$recent_stratum_log" | grep -qiE 'dead lock|exiting|segmentation fault|aborted|panic|fatal'; then
    fail "recent Stratum log contains a fatal exit signal; inspect $VEX_STRATUM_CONFIG_DIR/vexon.log"
  elif printf '%s\n' "$recent_stratum_log" | grep -qiE 'bad mining address|ERROR|getwork|not connected'; then
    warn "recent Stratum log contains warnings; inspect $VEX_STRATUM_CONFIG_DIR/vexon.log"
  else
    ok "recent Stratum log has no obvious errors"
  fi
else
  fail "missing Stratum log: $VEX_STRATUM_CONFIG_DIR/vexon.log"
fi

echo
echo "== web filesystem =="
check_web_writable "$VEX_WEB_DIR/vexon/runtime" "Yii runtime directory"
check_web_writable "$VEX_WEB_DIR/vexon/runtime/cookies" "Yii cookie runtime directory"
check_web_writable "$VEX_LOG_DIR" "Vexon web log directory"
check_web_writable "$VEX_LOG_DIR/debug.log" "Vexon debug log"

echo
echo "== web =="
check_http "$POOL_DOMAIN" /
check_http "$POOL_DOMAIN" /explorer
check_http "$POOL_DOMAIN" /site/mining
check_http "$POOL_DOMAIN" /site/api
check_http "$VEX_DOCS_DOMAIN" /

echo
echo "== rendered pages =="
check_rendered_page "$POOL_DOMAIN" "/" "Pool overview"
check_rendered_page "$POOL_DOMAIN" "/explorer" "Explorer"
check_rendered_page "$POOL_DOMAIN" "/explorer?q=0" "Explorer height lookup"
if [[ -n "$VEX_ADDRESS" ]]; then
  check_rendered_page "$POOL_DOMAIN" "/explorer?address=${VEX_ADDRESS}" "Explorer address lookup"
else
  warn "VEX_ADDRESS is empty; skipped Explorer address lookup"
fi
check_rendered_page "$POOL_DOMAIN" "/site/mining" "Mining dashboard"
check_rendered_page "$POOL_DOMAIN" "/site/api" "API documentation page"
check_rendered_page "$POOL_DOMAIN" "/site/current_results" "Pool status panel"
check_rendered_page "$POOL_DOMAIN" "/site/mining_results" "Mining accounting panel"
check_rendered_page "$POOL_DOMAIN" "/site/found_results" "Block rewards panel"
check_rendered_page "$VEX_DOCS_DOMAIN" "/" "Documentation site"

echo
echo "== api =="
check_json_api "$POOL_DOMAIN" "/api/pool" "Pool JSON API"
check_json_api "$POOL_DOMAIN" "/api/diagnostics" "Diagnostics JSON API"
check_json_api "$POOL_DOMAIN" "/api/explorer" "Explorer overview JSON API"
check_json_api "$POOL_DOMAIN" "/api/explorer?q=0" "Explorer height JSON API"
check_json_api "$POOL_DOMAIN" "/api/walletsync" "Wallet sync JSON API"
if [[ -n "$VEX_ADDRESS" ]]; then
  check_json_api "$POOL_DOMAIN" "/api/address?address=${VEX_ADDRESS}" "Address JSON API"
  check_json_api "$POOL_DOMAIN" "/api/walletsync?address=${VEX_ADDRESS}" "Wallet sync address JSON API"
else
  warn "VEX_ADDRESS is empty; skipped Address JSON API"
fi
check_broadcast_api "$POOL_DOMAIN" "Broadcast JSON API"

echo
echo "== result =="
if (( failures > 0 )); then
  echo "Health check failed with $failures failure(s) and $warnings warning(s)."
  exit 1
fi

echo "Health check passed with $warnings warning(s)."
