#!/usr/bin/env bash
set -u

MODE="check"
TARGET_HOST="${WINDOWS99_HOST:-192.168.0.99}"
CONNECT_TIMEOUT="${WINDOWS99_CONNECT_TIMEOUT:-3}"
SSH_TIMEOUT="${WINDOWS99_SSH_TIMEOUT:-3}"
SSH_PORT="${WINDOWS99_SSH_PORT:-22}"
MAX_AUTH_USERS="${WINDOWS99_MAX_AUTH_USERS:-2}"
MAX_AUTH_USERS_EXPLICIT=0
if [[ -n "${WINDOWS99_MAX_AUTH_USERS:-}" ]]; then
  MAX_AUTH_USERS_EXPLICIT=1
fi
KNOWN_HOSTS_FILE="${WINDOWS99_KNOWN_HOSTS_FILE:-/tmp/awoooi-windows99-known_hosts}"
REMOTE_VERIFY_COMMAND="${WINDOWS99_REMOTE_VERIFY_COMMAND:-powershell -NoProfile -ExecutionPolicy Bypass -File .\\windows99-vmware-autostart.ps1 -Mode Verify}"
SSH_USERS=(ogt wooo ooo administrator Administrator)
SSH_USERS_EXPLICIT=0

if [[ -n "${WINDOWS99_SSH_USERS:-}" ]]; then
  # shellcheck disable=SC2206
  SSH_USERS=(${WINDOWS99_SSH_USERS})
  SSH_USERS_EXPLICIT=1
fi

is_positive_int() {
  [[ "$1" =~ ^[1-9][0-9]*$ ]]
}

if ! is_positive_int "${CONNECT_TIMEOUT}"; then
  CONNECT_TIMEOUT=3
fi
if ! is_positive_int "${SSH_TIMEOUT}"; then
  SSH_TIMEOUT=3
fi
if ! is_positive_int "${MAX_AUTH_USERS}"; then
  MAX_AUTH_USERS=2
fi

usage() {
  printf '%s\n' "usage: $0 [--check|--collect] [--host HOST] [--users 'u1 u2'] [--timeout SECONDS]"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --check)
      MODE="check"
      ;;
    --collect)
      MODE="collect"
      ;;
    --host)
      shift
      TARGET_HOST="${1:-}"
      ;;
    --users)
      shift
      # shellcheck disable=SC2206
      SSH_USERS=(${1:-})
      SSH_USERS_EXPLICIT=1
      ;;
    --timeout)
      shift
      CONNECT_TIMEOUT="${1:-5}"
      SSH_TIMEOUT="${CONNECT_TIMEOUT}"
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      printf '%s\n' "error=unknown_argument:$1" >&2
      usage >&2
      exit 64
      ;;
  esac
  shift
done

if ! is_positive_int "${CONNECT_TIMEOUT}"; then
  CONNECT_TIMEOUT=3
fi
if ! is_positive_int "${SSH_TIMEOUT}"; then
  SSH_TIMEOUT=3
fi
if ! is_positive_int "${MAX_AUTH_USERS}"; then
  MAX_AUTH_USERS=2
fi
if [[ "${SSH_USERS_EXPLICIT}" == "1" && "${MAX_AUTH_USERS_EXPLICIT}" != "1" ]]; then
  MAX_AUTH_USERS="${#SSH_USERS[@]}"
fi
if ! is_positive_int "${MAX_AUTH_USERS}"; then
  MAX_AUTH_USERS=2
fi

if [[ "${MODE}" != "check" && "${MODE}" != "collect" ]]; then
  printf '%s\n' "error=invalid_mode:${MODE}" >&2
  exit 64
fi

PORT_TIMEOUT_WRAPPER="none"
if command -v timeout >/dev/null 2>&1; then
  PORT_TIMEOUT_WRAPPER="timeout"
elif command -v gtimeout >/dev/null 2>&1; then
  PORT_TIMEOUT_WRAPPER="gtimeout"
fi

port_open() {
  local port="$1"
  if ! command -v nc >/dev/null 2>&1; then
    return 1
  fi
  if [[ "${PORT_TIMEOUT_WRAPPER}" == "timeout" ]]; then
    timeout "$((CONNECT_TIMEOUT + 1))s" nc -z -w "${CONNECT_TIMEOUT}" "${TARGET_HOST}" "${port}" >/dev/null 2>&1
  elif [[ "${PORT_TIMEOUT_WRAPPER}" == "gtimeout" ]]; then
    gtimeout "$((CONNECT_TIMEOUT + 1))s" nc -z -w "${CONNECT_TIMEOUT}" "${TARGET_HOST}" "${port}" >/dev/null 2>&1
  else
    nc -z -w "${CONNECT_TIMEOUT}" "${TARGET_HOST}" "${port}" >/dev/null 2>&1
  fi
}

bool_for_port() {
  local port="$1"
  if port_open "${port}"; then
    printf '1'
  else
    printf '0'
  fi
}

join_users() {
  local joined=""
  local user
  for user in "${SSH_USERS[@]}"; do
    if [[ -z "${joined}" ]]; then
      joined="${user}"
    else
      joined="${joined},${user}"
    fi
  done
  printf '%s' "${joined}"
}

PORT_22_OPEN="$(bool_for_port 22)"
PORT_3389_OPEN="$(bool_for_port 3389)"
PORT_5985_OPEN="$(bool_for_port 5985)"
PORT_5986_OPEN="$(bool_for_port 5986)"
PORT_9182_OPEN="$(bool_for_port 9182)"

SSH_BATCHMODE_AUTH_READY=0
SSH_AUTHENTICATED_USER=""
SSH_AUTH_PROBE_EXIT_STATUS="not_attempted"
SSH_AUTH_PROBE_STDOUT_PRESENT=0
SSH_AUTH_ATTEMPTED_USERS="$(join_users)"
SSH_AUTH_PROBED_USERS=0
SSH_TIMEOUT_WRAPPER="none"
if command -v timeout >/dev/null 2>&1; then
  SSH_TIMEOUT_WRAPPER="timeout"
elif command -v gtimeout >/dev/null 2>&1; then
  SSH_TIMEOUT_WRAPPER="gtimeout"
fi

SSH_OPTS=(
  -o BatchMode=yes
  -o PreferredAuthentications=publickey
  -o PubkeyAuthentication=yes
  -o PasswordAuthentication=no
  -o KbdInteractiveAuthentication=no
  -o NumberOfPasswordPrompts=0
  -o ConnectTimeout="${SSH_TIMEOUT}"
  -o ConnectionAttempts=1
  -o GSSAPIAuthentication=no
  -o LogLevel=ERROR
  -o StrictHostKeyChecking=no
  -o UserKnownHostsFile="${KNOWN_HOSTS_FILE}"
  -p "${SSH_PORT}"
)

run_ssh() {
  local user="$1"
  shift
  if [[ "${SSH_TIMEOUT_WRAPPER}" == "timeout" ]]; then
    timeout "$((SSH_TIMEOUT + 1))s" ssh "${SSH_OPTS[@]}" "${user}@${TARGET_HOST}" "$@"
  elif [[ "${SSH_TIMEOUT_WRAPPER}" == "gtimeout" ]]; then
    gtimeout "$((SSH_TIMEOUT + 1))s" ssh "${SSH_OPTS[@]}" "${user}@${TARGET_HOST}" "$@"
  else
    ssh "${SSH_OPTS[@]}" "${user}@${TARGET_HOST}" "$@"
  fi
}

if [[ "${PORT_22_OPEN}" == "1" ]]; then
  for user in "${SSH_USERS[@]}"; do
    if [[ "${SSH_AUTH_PROBED_USERS}" -ge "${MAX_AUTH_USERS}" ]]; then
      break
    fi
    SSH_AUTH_PROBED_USERS=$((SSH_AUTH_PROBED_USERS + 1))
    auth_output=""
    if auth_output="$(run_ssh "${user}" "echo AWOOOI_WINDOWS99_SSH_READY" 2>&1)"; then
      SSH_BATCHMODE_AUTH_READY=1
      SSH_AUTHENTICATED_USER="${user}"
      SSH_AUTH_PROBE_EXIT_STATUS=0
      if [[ -n "${auth_output}" ]]; then
        SSH_AUTH_PROBE_STDOUT_PRESENT=1
      fi
      break
    else
      SSH_AUTH_PROBE_EXIT_STATUS=$?
    fi
  done
fi

DRY_RUN="true"
REMOTE_VERIFY_ATTEMPTED=0
REMOTE_VERIFY_EXIT_STATUS="not_attempted"
VERIFY_COLLECTION_STATUS="blocked_ssh_publickey_auth_missing"
SAFE_NEXT_STEP="select_existing_authorized_public_key_user_or_set_WINDOWS99_SSH_USERS_then_rerun_collector_no_password"
REMOTE_VERIFY_OUTPUT=""
PROCESS_EXIT_STATUS=0

if [[ "${PORT_22_OPEN}" != "1" ]]; then
  VERIFY_COLLECTION_STATUS="blocked_ssh_port_closed"
  SAFE_NEXT_STEP="enable_existing_ssh_management_channel_publickey_only_then_rerun_collector_no_secret"
  if [[ "${MODE}" == "collect" ]]; then
    PROCESS_EXIT_STATUS=75
  fi
elif [[ "${SSH_BATCHMODE_AUTH_READY}" != "1" ]]; then
  VERIFY_COLLECTION_STATUS="blocked_ssh_publickey_auth_missing"
  SAFE_NEXT_STEP="select_existing_authorized_public_key_user_or_set_WINDOWS99_SSH_USERS_then_rerun_collector_no_password"
  if [[ "${MODE}" == "collect" ]]; then
    PROCESS_EXIT_STATUS=75
  fi
elif [[ "${MODE}" == "check" ]]; then
  VERIFY_COLLECTION_STATUS="ready_ssh_batchmode_auth_probe_only"
  SAFE_NEXT_STEP="rerun_collector_with_collect_then_commit_no_secret_verify_artifact_and_scorecard_rerun"
else
  DRY_RUN="false"
  REMOTE_VERIFY_ATTEMPTED=1
  if REMOTE_VERIFY_OUTPUT="$(run_ssh "${SSH_AUTHENTICATED_USER}" "${REMOTE_VERIFY_COMMAND}" 2>&1)"; then
    REMOTE_VERIFY_EXIT_STATUS=0
    VERIFY_COLLECTION_STATUS="collected_windows99_vmware_verify_stdout"
    SAFE_NEXT_STEP="commit_no_secret_verify_artifact_then_rerun_reboot_auto_recovery_slo_scorecard"
  else
    REMOTE_VERIFY_EXIT_STATUS=$?
    VERIFY_COLLECTION_STATUS="blocked_remote_verify_command_failed"
    SAFE_NEXT_STEP="inspect_no_secret_verify_stdout_then_fix_verify_script_or_path_and_rerun_collector"
    PROCESS_EXIT_STATUS=75
  fi
fi

printf '%s\n' "schema_version=windows99_vmware_verify_collector_v1"
printf '%s\n' "dry_run=${DRY_RUN}"
printf '%s\n' "target_host=${TARGET_HOST}"
printf '%s\n' "target_host_alias=99"
printf '%s\n' "connect_timeout_seconds=${CONNECT_TIMEOUT}"
printf '%s\n' "ssh_timeout_seconds=${SSH_TIMEOUT}"
printf '%s\n' "port_timeout_wrapper=${PORT_TIMEOUT_WRAPPER}"
printf '%s\n' "ssh_auth_probe_user_limit=${MAX_AUTH_USERS}"
printf '%s\n' "ssh_timeout_wrapper=${SSH_TIMEOUT_WRAPPER}"
printf '%s\n' "port_22_open=${PORT_22_OPEN}"
printf '%s\n' "port_3389_open=${PORT_3389_OPEN}"
printf '%s\n' "port_5985_open=${PORT_5985_OPEN}"
printf '%s\n' "port_5986_open=${PORT_5986_OPEN}"
printf '%s\n' "port_9182_open=${PORT_9182_OPEN}"
printf '%s\n' "ssh_candidate_users=${SSH_AUTH_ATTEMPTED_USERS}"
printf '%s\n' "ssh_auth_probed_users=${SSH_AUTH_PROBED_USERS}"
printf '%s\n' "ssh_batchmode_auth_ready=${SSH_BATCHMODE_AUTH_READY}"
printf '%s\n' "ssh_authenticated_user=${SSH_AUTHENTICATED_USER}"
printf '%s\n' "ssh_auth_probe_exit_status=${SSH_AUTH_PROBE_EXIT_STATUS}"
printf '%s\n' "ssh_auth_probe_stdout_present=${SSH_AUTH_PROBE_STDOUT_PRESENT}"
printf '%s\n' "remote_verify_attempted=${REMOTE_VERIFY_ATTEMPTED}"
printf '%s\n' "remote_verify_exit_status=${REMOTE_VERIFY_EXIT_STATUS}"
printf '%s\n' "verify_collection_status=${VERIFY_COLLECTION_STATUS}"
printf '%s\n' "safe_next_step=${SAFE_NEXT_STEP}"
printf '%s\n' "secret_value_read=false"
printf '%s\n' "password_prompt_allowed=false"
printf '%s\n' "remote_write_performed=false"
printf '%s\n' "host_reboot_performed=false"
printf '%s\n' "vm_power_change_performed=false"
printf '%s\n' "windows_update_policy_apply_performed=false"

if [[ "${REMOTE_VERIFY_ATTEMPTED}" == "1" ]]; then
  printf '%s\n' "remote_verify_output_begin"
  printf '%s\n' "${REMOTE_VERIFY_OUTPUT}"
  printf '%s\n' "remote_verify_output_end"
fi

exit "${PROCESS_EXIT_STATUS}"
