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

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

FIX=0
RUN_HEALTH=1

usage() {
  cat <<'USAGE'
Usage:
  scripts/doctor.sh
  sudo scripts/doctor.sh --fix

Modes:
  default   Check common post-deploy problems and print repair commands.
  --fix     Apply safe repairs: permissions, web deploy, nginx/php-fpm reload,
            VEX coin database settings, Stratum restart, wallet RPC restart
            when VEX_START_WALLET=1.

Options:
  --no-health   Skip the final scripts/health-check.sh pass.
  -h, --help    Show this help.
USAGE
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --fix)
      FIX=1
      ;;
    --no-health)
      RUN_HEALTH=0
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
  shift
done

failures=0
warnings=0
repairs=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)); }
fixlog() { printf '[FIX] %s\n' "$1"; repairs=$((repairs + 1)); }
todo() { printf '[TODO] %s\n' "$1"; warnings=$((warnings + 1)); }
have() { command -v "$1" >/dev/null 2>&1; }
as_root() { [[ "$(id -u)" == "0" ]]; }

run_fix() {
  local description="$1"
  shift

  if (( FIX )); then
    fixlog "$description"
    "$@"
  else
    todo "$description (run again with --fix)"
  fi
}

if [[ ! -f .env ]]; then
  fail "missing .env in $repo_root"
else
  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_NODE_APPDATA="${VEX_NODE_APPDATA:-/root/.vexond}"
VEX_WALLET_APPDATA="${VEX_WALLET_APPDATA:-/root/.vexonwallet}"
VEX_RPC_PORT="${VEX_RPC_PORT:-18556}"
VEX_WALLET_RPC_PORT="${VEX_WALLET_RPC_PORT:-18557}"
VEX_RPC_USER="${VEX_RPC_USER:-}"
VEX_RPC_PASSWORD="${VEX_RPC_PASSWORD:-}"
VEX_WALLET_RPC_USER="${VEX_WALLET_RPC_USER:-}"
VEX_WALLET_RPC_PASSWORD="${VEX_WALLET_RPC_PASSWORD:-}"
VEX_WALLET_PASSPHRASE="${VEX_WALLET_PASSPHRASE:-}"
VEX_START_WALLET="${VEX_START_WALLET:-0}"
VEX_WEB_USER="${VEX_WEB_USER:-www-data}"
VEX_WEB_GROUP="${VEX_WEB_GROUP:-www-data}"
VEX_CONFIGURE_NGINX="${VEX_CONFIGURE_NGINX:-0}"
STRATUM_DIFFICULTY="${STRATUM_DIFFICULTY:-256}"
VEX_BLOCK_SUBMIT_DIFF="${VEX_BLOCK_SUBMIT_DIFF:-503382015}"
VEX_ADDRESS="${VEX_ADDRESS:-}"
VEX_MIN_PAYOUT="${VEX_MIN_PAYOUT:-0.001}"
VEX_MATURE_BLOCKS="${VEX_MATURE_BLOCKS:-100}"
VEX_TXFEE="${VEX_TXFEE:-0}"
VEX_STRATUM_SCREEN="${VEX_STRATUM_SCREEN:-Vexon_Stratum}"

port_listening() {
  local port="$1"
  have ss && ss -lnt | awk '{print $4}' | grep -Eq "[:.]${port}$"
}

service_active() {
  local name="$1"
  have systemctl && systemctl is-active --quiet "$name"
}

php_fpm_units() {
  have systemctl || return 0
  systemctl list-unit-files --type=service 'php*-fpm.service' 'php-fpm.service' --no-legend 2>/dev/null | awk '{print $1}'
}

chmod_env() {
  chmod 600 .env
}

repair_web_permissions() {
  mkdir -p \
    "$VEX_WEB_DIR/assets" \
    "$VEX_WEB_DIR/vexon/runtime" \
    "$VEX_WEB_DIR/vexon/runtime/cookies" \
    "$VEX_DOCS_DIR" \
    "$VEX_LOG_DIR"

  touch "$VEX_LOG_DIR/debug.log" "$VEX_LOG_DIR/rental.log"
  chmod -R u+rwX,go+rX "$VEX_WEB_DIR" "$VEX_DOCS_DIR" 2>/dev/null || true
  chmod -R 775 "$VEX_WEB_DIR/vexon/runtime" "$VEX_WEB_DIR/assets" 2>/dev/null || true
  chmod 775 "$VEX_LOG_DIR"
  chmod 664 "$VEX_LOG_DIR/debug.log" "$VEX_LOG_DIR/rental.log"

  if id "$VEX_WEB_USER" >/dev/null 2>&1 && getent group "$VEX_WEB_GROUP" >/dev/null 2>&1; then
    chown -R "$VEX_WEB_USER:$VEX_WEB_GROUP" "$VEX_WEB_DIR" "$VEX_DOCS_DIR" "$VEX_LOG_DIR"
  fi
}

reload_php_fpm() {
  local unit
  while read -r unit; do
    [[ -n "$unit" ]] || continue
    systemctl reload "$unit" >/dev/null 2>&1 || systemctl restart "$unit" >/dev/null 2>&1 || true
  done < <(php_fpm_units)
}

start_nginx_if_needed() {
  nginx -t
  if have systemctl; then
    if service_active nginx; then
      systemctl reload nginx
    else
      systemctl start nginx
    fi
  fi
}

start_memcached_if_needed() {
  if have systemctl; then
    systemctl start memcached >/dev/null 2>&1 || true
  fi
}

render_web() {
  bash scripts/deploy-web.sh
}

repair_coin() {
  bash scripts/repair-vex-coin.sh
}

start_wallet_rpc() {
  bash scripts/start-wallet-rpc.sh
}

start_pool_stack() {
  bash scripts/start-pool.sh
}

check_file_exists() {
  local path="$1"
  local label="$2"
  if [[ -e "$path" ]]; then
    ok "$label exists: $path"
  else
    fail "$label is missing: $path"
  fi
}

check_writable_by_web_user() {
  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
  elif [[ -w "$path" ]]; then
    ok "$label is writable by current user"
  else
    fail "$label is not writable: $path"
  fi
}

http_code() {
  local host="$1"
  local path="$2"
  curl -k -L --max-time 20 -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1${path}" -H "Host: ${host}" 2>/dev/null || true
}

render_probe() {
  local host="$1"
  local path="$2"
  local label="$3"
  local tmp code bytes
  tmp="/tmp/vexon-doctor-page.$$.$RANDOM"
  : > "$tmp"
  code="$(curl -k -L --max-time 20 -sS -o "$tmp" -w '%{http_code}' "http://127.0.0.1${path}" -H "Host: ${host}" 2>/dev/null || true)"
  bytes="$(wc -c < "$tmp" 2>/dev/null | tr -d ' ' || echo 0)"

  if [[ "$code" =~ ^2 && "$bytes" =~ ^[0-9]+$ ]] && (( bytes > 20 )) && ! grep -qiE 'Internal Server Error|Permission denied|Trying to access array offset|Fatal error|Parse error|failed to open stream' "$tmp"; then
    ok "$label renders (${bytes} bytes)"
  else
    fail "$label render problem: http=${code:-none} bytes=${bytes:-0}"
  fi

  rm -f "$tmp"
}

echo "== Vexon Pool doctor =="
date
if (( FIX )); then
  echo "Mode: repair"
  if ! as_root; then
    fail "--fix should be run as root on the server"
  fi
else
  echo "Mode: check-only"
fi

echo
echo "== environment =="
if [[ -f .env ]]; then
  env_mode="$(stat -c '%a' .env 2>/dev/null || stat -f '%Lp' .env 2>/dev/null || echo unknown)"
  if [[ "$env_mode" == "600" ]]; then
    ok ".env permissions are 600"
  else
    warn ".env permissions are $env_mode; production secrets should be chmod 600"
    run_fix "lock down .env permissions" chmod_env
  fi
fi

[[ "$VEX_WEB_DIR" != "/var/web" && "$VEX_STRATUM_CONFIG_DIR" != "/var/stratum/config" ]] \
  && ok "Vexon paths are isolated from legacy pool paths" \
  || fail "Vexon paths overlap legacy pool paths; check .env"

echo
echo "== required commands =="
for cmd in bash rsync curl mysql screen ss; do
  have "$cmd" && ok "command available: $cmd" || fail "missing command: $cmd"
done

echo
echo "== web deploy and permissions =="
check_file_exists files/var-web/index.php "source pool web"
check_file_exists files/docs-site/index.html "source docs site"
if [[ ! -f "$VEX_WEB_DIR/index.php" || ! -f "$VEX_WEB_DIR/serverconfig.php" || ! -f "$VEX_DOCS_DIR/index.html" ]]; then
  warn "deployed web files look incomplete"
  run_fix "deploy pool/docs web files and render serverconfig.php" render_web
elif (( FIX )); then
  run_fix "redeploy pool/docs web files and refresh serverconfig.php" render_web
else
  ok "deployed web files are present"
fi

if [[ -e "$VEX_WEB_DIR/vexon/runtime" || -e "$VEX_LOG_DIR/debug.log" ]]; then
  check_writable_by_web_user "$VEX_WEB_DIR/vexon/runtime" "Yii runtime directory"
  check_writable_by_web_user "$VEX_WEB_DIR/vexon/runtime/cookies" "Yii cookies directory"
  check_writable_by_web_user "$VEX_LOG_DIR" "web log directory"
  check_writable_by_web_user "$VEX_LOG_DIR/debug.log" "debug.log"
else
  warn "runtime/log paths have not been initialized"
fi
if (( FIX )); then
  run_fix "repair Yii runtime, asset, docs, and log permissions" repair_web_permissions
fi

if [[ -f "$VEX_WEB_DIR/favicon.png" && -f "$VEX_WEB_DIR/favicon.ico" ]]; then
  ok "pool favicons are deployed"
else
  warn "pool favicon files are missing or stale"
  run_fix "redeploy web files to restore Vexon favicon" render_web
fi

echo
echo "== web services =="
if have nginx; then
  if nginx -t >/tmp/vexon-doctor-nginx.$$ 2>&1; then
    ok "nginx config syntax is valid"
    if have systemctl; then
      if service_active nginx; then
        ok "nginx is active"
        (( FIX )) && run_fix "reload nginx" systemctl reload nginx
      else
        warn "nginx is installed but inactive"
        run_fix "start nginx" start_nginx_if_needed
      fi
    fi
  else
    fail "nginx config syntax failed: $(cat /tmp/vexon-doctor-nginx.$$)"
  fi
  rm -f /tmp/vexon-doctor-nginx.$$
else
  warn "nginx is not installed; skip when Baota or another panel owns web serving"
fi

php_sock="$(find /run/php -name 'php*-fpm.sock' -o -name 'php-fpm.sock' 2>/dev/null | head -1 || true)"
if [[ -n "$php_sock" ]]; then
  ok "PHP-FPM socket exists: $php_sock"
else
  warn "PHP-FPM socket was not found under /run/php"
  run_fix "start/reload php-fpm services" reload_php_fpm
fi
if (( FIX )); then
  run_fix "reload php-fpm to clear opcode/runtime cache" reload_php_fpm
fi

if port_listening 11211 || service_active memcached; then
  ok "memcached is available"
else
  warn "memcached is not active; pool panels may render stale or empty data"
  run_fix "start memcached" start_memcached_if_needed
fi

echo
echo "== Vexon services =="
if port_listening "$VEX_RPC_PORT"; then
  ok "Vexon node RPC is listening on $VEX_RPC_PORT"
else
  warn "Vexon node RPC is not listening on $VEX_RPC_PORT"
  run_fix "start Vexon pool stack" start_pool_stack
fi

if port_listening "$STRATUM_PORT"; then
  ok "Vexon Stratum is listening on $STRATUM_PORT"
else
  warn "Vexon Stratum is not listening on $STRATUM_PORT"
  run_fix "repair VEX coin row and restart Stratum" repair_coin
fi

if [[ "$VEX_START_WALLET" == "1" ]]; then
  if [[ -z "$VEX_WALLET_PASSPHRASE" ]]; then
    fail "VEX_START_WALLET=1 but VEX_WALLET_PASSPHRASE is empty"
  elif [[ ! -f "$VEX_WALLET_APPDATA/mainnet/wallet.db" ]]; then
    fail "wallet database is missing: $VEX_WALLET_APPDATA/mainnet/wallet.db"
  elif port_listening "$VEX_WALLET_RPC_PORT"; then
    ok "Vexon wallet RPC is listening on $VEX_WALLET_RPC_PORT"
  else
    warn "wallet RPC is enabled but not listening on $VEX_WALLET_RPC_PORT"
    run_fix "start Vexon wallet RPC" start_wallet_rpc
  fi
else
  warn "VEX_START_WALLET=0; automatic chain payouts stay disabled"
fi

echo
echo "== node and wallet RPC probes =="
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" getwork >/dev/null 2>&1; then
    ok "node can produce getwork templates"
  else
    fail "node cannot produce getwork templates"
  fi
else
  fail "missing vexonctl or node RPC credentials"
fi

if [[ "$VEX_START_WALLET" == "1" && -x "$VEX_BIN_DIR/vexonctl" && -n "$VEX_WALLET_RPC_USER" && -n "$VEX_WALLET_RPC_PASSWORD" ]]; then
  if "$VEX_BIN_DIR/vexonctl" --wallet --notls --rpcserver "127.0.0.1:${VEX_WALLET_RPC_PORT}" -u "$VEX_WALLET_RPC_USER" -P "$VEX_WALLET_RPC_PASSWORD" getbalance >/dev/null 2>&1; then
    ok "wallet RPC getbalance works"
  else
    fail "wallet RPC getbalance failed"
  fi
fi

echo
echo "== database repair targets =="
if have mysql && mysql -uroot "$POOL_DB_NAME" -Nse "SELECT 1" >/dev/null 2>&1; then
  ok "database is reachable: $POOL_DB_NAME"
  coin_row="$(mysql -uroot "$POOL_DB_NAME" -Nse "SELECT CONCAT_WS('|', enable, auto_ready, algo, rpcencoding, rpcport, COALESCE(payout_min,''), COALESCE(mature_blocks,''), COALESCE(txfee,''), COALESCE(master_wallet,'')) FROM coins WHERE symbol='VEX' LIMIT 1" 2>/dev/null || true)"
  if [[ -z "$coin_row" ]]; then
    fail "missing VEX coin row"
    run_fix "repair/create expected VEX coin row" repair_coin
  else
    IFS='|' read -r coin_enable coin_auto_ready coin_algo coin_rpcencoding coin_rpcport coin_payout_min coin_mature coin_txfee coin_wallet <<< "$coin_row"
    ok "VEX coin row: enable=$coin_enable auto_ready=$coin_auto_ready algo=$coin_algo rpcencoding=$coin_rpcencoding rpcport=$coin_rpcport payout_min=$coin_payout_min mature_blocks=$coin_mature txfee=$coin_txfee"
    if [[ "$coin_enable" != "1" || "$coin_auto_ready" != "1" || "$coin_algo" != "decred" || "$coin_rpcencoding" != "DCR" || "$coin_rpcport" != "$VEX_RPC_PORT" || "$coin_payout_min" != "$VEX_MIN_PAYOUT" || "$coin_mature" != "$VEX_MATURE_BLOCKS" || "$coin_wallet" != "$VEX_ADDRESS" ]]; then
      warn "VEX coin row drifted from .env expected values"
      run_fix "repair VEX coin database row and restart Stratum" repair_coin
    fi
  fi
else
  fail "database is not reachable: $POOL_DB_NAME"
fi

echo
echo "== HTTP smoke tests =="
if have curl; then
  for route in "/" "/explorer" "/site/mining"; do
    code="$(http_code "$POOL_DOMAIN" "$route")"
    [[ "$code" =~ ^(2|3) ]] && ok "HTTP ${POOL_DOMAIN}${route} returned $code" || fail "HTTP ${POOL_DOMAIN}${route} returned ${code:-no response}"
  done
  code="$(http_code "$VEX_DOCS_DOMAIN" "/")"
  [[ "$code" =~ ^(2|3) ]] && ok "HTTP ${VEX_DOCS_DOMAIN}/ returned $code" || fail "HTTP ${VEX_DOCS_DOMAIN}/ returned ${code:-no response}"

  render_probe "$POOL_DOMAIN" "/" "Pool overview"
  render_probe "$POOL_DOMAIN" "/explorer" "Explorer"
  render_probe "$POOL_DOMAIN" "/site/mining" "Mining dashboard"
  render_probe "$VEX_DOCS_DOMAIN" "/" "Docs site"
else
  warn "curl missing; skipped HTTP smoke tests"
fi

if (( RUN_HEALTH )); then
  echo
  echo "== final health-check.sh =="
  if bash scripts/health-check.sh; then
    ok "final health check passed"
  else
    fail "final health check reported issues"
  fi
fi

echo
echo "== result =="
if (( failures > 0 )); then
  echo "Doctor finished with $failures failure(s), $warnings warning(s), $repairs repair action(s)."
  if (( ! FIX )); then
    echo "Re-run with: sudo scripts/doctor.sh --fix"
  fi
  exit 1
fi

echo "Doctor finished with $warnings warning(s), $repairs repair action(s)."
if (( ! FIX )); then
  echo "No changes were made. Use --fix to apply safe repairs."
fi
