#!/usr/bin/env bash
if [ -z "${BASH_VERSION:-}" ]; then
  if command -v bash >/dev/null 2>&1; then
    exec bash -s -- "$@"
  fi
  echo "error: bash is required to run this installer"
  exit 1
fi
set -euo pipefail

export DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"
export PYTHONWARNINGS="${PYTHONWARNINGS:-ignore::SyntaxWarning}"

COORDINATOR_URL=""
ENROLL_TOKEN=""
NODE_NAME=""
EXIT_NODE="false"
REPO_URL="https://repo.rizomasec.ru"
CHANNEL="stable"
PROFILE="mesh"
RUNTIME_MODE="host"
ENABLE_FIREWALL="false"
ENABLE_ANTIVIRUS="false"
IDENTITY_PATH="/etc/rizoma/agent_identity.json"
REUSE_EXISTING_IDENTITY="false"
FORCE_REENROLL="false"
AGENT_HTTP_LISTEN="${RIZOMA_AGENT_LISTEN:-127.0.0.1:8081}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --coordinator-url)
      COORDINATOR_URL="$2"
      shift 2
      ;;
    --token)
      ENROLL_TOKEN="$2"
      shift 2
      ;;
    --name)
      NODE_NAME="$2"
      shift 2
      ;;
    --exit-node)
      EXIT_NODE="true"
      shift
      ;;
    --profile)
      PROFILE="$2"
      shift 2
      ;;
    --runtime-mode|--runtime)
      RUNTIME_MODE="$2"
      shift 2
      ;;
    --enable-firewall)
      ENABLE_FIREWALL="true"
      shift
      ;;
    --enable-antivirus)
      ENABLE_ANTIVIRUS="true"
      shift
      ;;
    --repo-url)
      REPO_URL="$2"
      shift 2
      ;;
    --channel)
      CHANNEL="$2"
      shift 2
      ;;
    --identity)
      IDENTITY_PATH="$2"
      shift 2
      ;;
    --reuse-existing-identity)
      REUSE_EXISTING_IDENTITY="true"
      shift
      ;;
    --force-reenroll)
      FORCE_REENROLL="true"
      shift
      ;;
    *)
      shift
      ;;
  esac
done

if [[ -z "$COORDINATOR_URL" ]]; then
  echo "error: --coordinator-url is required"
  exit 1
fi

if [[ -z "$ENROLL_TOKEN" ]]; then
  echo "error: --token is required"
  exit 1
fi

if [[ -z "$IDENTITY_PATH" ]]; then
  echo "error: --identity cannot be empty"
  exit 1
fi

if [[ "$REUSE_EXISTING_IDENTITY" == "true" && "$FORCE_REENROLL" == "true" ]]; then
  echo "error: choose either --reuse-existing-identity or --force-reenroll, not both"
  exit 1
fi

if [[ -z "$NODE_NAME" ]]; then
  NODE_NAME="$(hostname)"
fi

case "$PROFILE" in
  mesh)
    ;;
  firewall)
    ENABLE_FIREWALL="true"
    ;;
  antivirus)
    ENABLE_ANTIVIRUS="true"
    ;;
  security)
    ENABLE_FIREWALL="true"
    ENABLE_ANTIVIRUS="true"
    ;;
  *)
    echo "error: unsupported profile '$PROFILE' (expected: mesh, firewall, antivirus, security)"
    exit 1
    ;;
esac

case "$RUNTIME_MODE" in
  host)
    ;;
  *)
    echo "error: unsupported runtime mode '$RUNTIME_MODE' (expected: host)"
    exit 1
    ;;
esac

if [[ "$(id -u)" -ne 0 ]]; then
  if command -v sudo >/dev/null 2>&1; then
    SUDO="sudo"
  elif command -v doas >/dev/null 2>&1; then
    SUDO="doas"
  else
    echo "error: run as root or install sudo/doas"
    exit 1
  fi
else
  SUDO=""
fi

run_root() {
  if [[ -n "${SUDO:-}" ]]; then
    "$SUDO" "$@"
  else
    "$@"
  fi
}

write_root_file() {
  local path="$1"
  local mode="$2"
  local tmp
  tmp="$(mktemp /tmp/rizoma-write.XXXXXX)"
  cat > "$tmp"
  run_root install -m "$mode" "$tmp" "$path"
  rm -f "$tmp"
}

print_existing_identity_error() {
  local identity="$1"
  echo "error: existing agent identity found at $identity"
  echo "This installer creates a fresh peer by default and will not reuse an existing node ID, Mesh IP, or MagicDNS name."
  echo "For repair or reinstall of this same peer, rerun with --reuse-existing-identity."
  echo "For deliberate fresh enrollment on this host, rerun with --force-reenroll to back up the existing identity first."
}

reject_ambiguous_existing_identity() {
  local identity="$1"
  if ! run_root test -f "$identity"; then
    return
  fi
  if [[ "$REUSE_EXISTING_IDENTITY" == "true" || "$FORCE_REENROLL" == "true" ]]; then
    return
  fi

  print_existing_identity_error "$identity"
  exit 1
}

handle_existing_identity() {
  local identity="$1"
  if ! run_root test -f "$identity"; then
    return
  fi

  if [[ "$FORCE_REENROLL" == "true" ]]; then
    local backup
    backup="${identity}.backup.$(date -u +%Y%m%dT%H%M%SZ)"
    echo "Existing agent identity found at $identity; backing it up for fresh enrollment."
    if command -v systemctl >/dev/null 2>&1; then
      run_root systemctl stop rizoma-agent >/dev/null 2>&1 || true
    fi
    run_root mv "$identity" "$backup"
    echo "Previous agent identity backup: $backup"
    return
  fi

  if [[ "$REUSE_EXISTING_IDENTITY" == "true" ]]; then
    echo "Existing agent identity found at $identity; reusing it because --reuse-existing-identity was set."
    return
  fi

  print_existing_identity_error "$identity"
  exit 1
}

reject_ambiguous_existing_identity "$IDENTITY_PATH"

if ! command -v systemctl >/dev/null 2>&1 && [[ "$RUNTIME_MODE" == "host" ]]; then
  echo "error: this installer targets systemd Linux hosts with full mesh networking."
  exit 1
fi

if [[ ! -c /dev/net/tun ]] && command -v modprobe >/dev/null 2>&1; then
  run_root modprobe tun >/dev/null 2>&1 || true
fi

if [[ ! -c /dev/net/tun ]]; then
  echo "error: /dev/net/tun is missing. Install on a Linux host or privileged container with TUN and CAP_NET_ADMIN."
  exit 1
fi

prepare_firewall_runtime() {
  if command -v systemctl >/dev/null 2>&1; then
    if systemctl list-unit-files | grep -q '^ufw\.service'; then
      if systemctl is-active --quiet ufw 2>/dev/null; then
        echo "Disabling active UFW firewall..."
        run_root ufw --force disable >/dev/null 2>&1 || run_root systemctl disable --now ufw >/dev/null 2>&1 || true
      fi
      run_root systemctl disable ufw >/dev/null 2>&1 || true
    fi

    if systemctl list-unit-files | grep -q '^firewalld\.service'; then
      if systemctl is-active --quiet firewalld 2>/dev/null; then
        echo "Disabling active firewalld firewall..."
        run_root systemctl disable --now firewalld >/dev/null 2>&1 || true
      fi
      run_root systemctl disable firewalld >/dev/null 2>&1 || true
    fi
  fi
}

install_packages() {
  if [[ "$#" -eq 0 ]]; then
    return
  fi
  if command -v apt-get >/dev/null 2>&1; then
    run_root apt-get update -qq
    run_root apt-get install -y "$@"
  elif command -v dnf >/dev/null 2>&1; then
    run_root dnf -y makecache
    run_root dnf -y install "$@"
  elif command -v yum >/dev/null 2>&1; then
    run_root yum -y makecache
    run_root yum -y install "$@"
  else
    echo "error: no supported package manager (apt/dnf/yum)"
    exit 1
  fi
}

install_apt_keyring() {
  local tmp_key
  local tmp_out
  tmp_key="$(mktemp /tmp/rizoma-keyring.XXXXXX)"
  curl -fsSL "$REPO_URL/keys/rizoma-archive-keyring.gpg" -o "$tmp_key"
  if grep -q "BEGIN PGP PUBLIC KEY BLOCK" "$tmp_key"; then
    tmp_out="$(mktemp /tmp/rizoma-keyring-out.XXXXXX)"
    gpg --batch --yes --dearmor -o "$tmp_out" "$tmp_key"
    run_root install -m 0644 "$tmp_out" /usr/share/keyrings/rizoma-archive-keyring.gpg
    rm -f "$tmp_out"
  else
    run_root install -m 0644 "$tmp_key" /usr/share/keyrings/rizoma-archive-keyring.gpg
  fi
  rm -f "$tmp_key"
}

nftables_runtime_available() {
  if ! command -v nft >/dev/null 2>&1; then
    return 1
  fi
  local tmp
  tmp="$(mktemp /tmp/rizoma-nft-preflight.XXXXXX)"
  cat > "$tmp" <<'EOF'
destroy table inet rizoma_preflight
table inet rizoma_preflight {
  chain input {
    type filter hook input priority 0; policy accept;
  }
}
EOF
  if run_root nft -c -f "$tmp" >/dev/null 2>&1; then
    rm -f "$tmp"
    return 0
  fi
  rm -f "$tmp"
  return 1
}

FIREWALL_MODE="off"
AV_ENABLED="false"
UPDATE_COMMAND="use apt, dnf, or yum to upgrade rizoma-agent"
APT_FEATURE_PACKAGES=()
RPM_FEATURE_PACKAGES=()

if [[ "$ENABLE_FIREWALL" == "true" ]]; then
  FIREWALL_MODE="nftables"
  APT_FEATURE_PACKAGES+=(nftables fail2ban)
  RPM_FEATURE_PACKAGES+=(nftables fail2ban)
fi

if [[ "$ENABLE_ANTIVIRUS" == "true" ]]; then
  AV_ENABLED="true"
  APT_FEATURE_PACKAGES+=(clamav clamav-freshclam clamav-daemon)
  RPM_FEATURE_PACKAGES+=(clamav clamav-update clamav-server)
fi

# Install rizoma-agent via package manager
if command -v apt-get >/dev/null 2>&1; then
  UPDATE_COMMAND="apt update && apt upgrade"
  install_packages curl ca-certificates gnupg jq
  run_root mkdir -p /usr/share/keyrings
  install_apt_keyring
  write_root_file /etc/apt/sources.list.d/rizoma.list 0644 <<EOF
deb [signed-by=/usr/share/keyrings/rizoma-archive-keyring.gpg] $REPO_URL/apt/$CHANNEL ./
EOF
  install_packages rizoma-agent "${APT_FEATURE_PACKAGES[@]}"
elif command -v dnf >/dev/null 2>&1; then
  UPDATE_COMMAND="dnf upgrade"
  install_packages curl ca-certificates jq
  run_root mkdir -p /etc/pki/rpm-gpg
  run_root mkdir -p /etc/yum.repos.d
  tmp_key="$(mktemp /tmp/rizoma-rpm-key.XXXXXX)"
  curl -fsSL "$REPO_URL/keys/RPM-GPG-KEY-rizoma" -o "$tmp_key"
  run_root install -m 0644 "$tmp_key" /etc/pki/rpm-gpg/RPM-GPG-KEY-rizoma
  rm -f "$tmp_key"
  write_root_file /etc/yum.repos.d/rizoma.repo 0644 <<REPOEOF
[rizoma]
name=Rizoma
baseurl=$REPO_URL/dnf/$CHANNEL/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rizoma
REPOEOF
  install_packages rizoma-agent "${RPM_FEATURE_PACKAGES[@]}"
elif command -v yum >/dev/null 2>&1; then
  UPDATE_COMMAND="yum update"
  install_packages curl ca-certificates jq
  run_root mkdir -p /etc/pki/rpm-gpg
  run_root mkdir -p /etc/yum.repos.d
  tmp_key="$(mktemp /tmp/rizoma-rpm-key.XXXXXX)"
  curl -fsSL "$REPO_URL/keys/RPM-GPG-KEY-rizoma" -o "$tmp_key"
  run_root install -m 0644 "$tmp_key" /etc/pki/rpm-gpg/RPM-GPG-KEY-rizoma
  rm -f "$tmp_key"
  write_root_file /etc/yum.repos.d/rizoma.repo 0644 <<REPOEOF
[rizoma]
name=Rizoma
baseurl=$REPO_URL/dnf/$CHANNEL/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rizoma
REPOEOF
  install_packages rizoma-agent "${RPM_FEATURE_PACKAGES[@]}"
else
  echo "error: no supported package manager (apt/dnf/yum)"
  exit 1
fi

if [[ "$ENABLE_FIREWALL" == "true" ]]; then
  if nftables_runtime_available; then
    prepare_firewall_runtime
  else
    echo "warning: nftables is installed, but the kernel netlink interface is unavailable on this host."
    echo "warning: continuing enrollment with host firewall enforcement disabled."
    FIREWALL_MODE="off"
  fi
fi

# Configure agent
run_root install -d -m 700 /etc/rizoma
run_root install -d -m 755 /var/lib/rizoma /var/log/rizoma
run_root install -d -m 700 /var/lib/rizoma/agent
run_root install -d -m 750 /var/log/rizoma/agent
if [[ "$ENABLE_ANTIVIRUS" == "true" ]]; then
  run_root install -d -m 700 /var/lib/rizoma/agent/quarantine
fi

handle_existing_identity "$IDENTITY_PATH"

write_root_file /etc/rizoma/agent.env 0600 <<EOF
RIZOMA_AGENT_LISTEN=$AGENT_HTTP_LISTEN
RIZOMA_AGENT_COORDINATOR_URL=$COORDINATOR_URL
RIZOMA_AGENT_TOKEN=$ENROLL_TOKEN
RIZOMA_AGENT_NAME=$NODE_NAME
RIZOMA_AGENT_EXIT_NODE=$EXIT_NODE
RIZOMA_AGENT_RUNTIME_MODE=$RUNTIME_MODE
RIZOMA_AGENT_IDENTITY=$IDENTITY_PATH
RIZOMA_AGENT_STORE=/var/lib/rizoma/agent
# The agent API remains HTTP until mesh mTLS is wired; bind it to loopback.
RIZOMA_AGENT_INSECURE_HTTP=true
RIZOMA_AGENT_TUN=rizoma0
RIZOMA_AGENT_QUIC_LISTEN=${RIZOMA_AGENT_QUIC_LISTEN:-0.0.0.0:4242}
RIZOMA_AGENT_QUIC_PUBLIC_ADDR=${RIZOMA_AGENT_QUIC_PUBLIC_ADDR:-}
RIZOMA_AGENT_FIREWALL_MODE=$FIREWALL_MODE
RIZOMA_AGENT_AV_ENABLED=$AV_ENABLED
RIZOMA_AGENT_AV_SCAN_MODE=on-demand
RIZOMA_AGENT_AV_SCAN_PATHS=/
RIZOMA_AGENT_AV_QUARANTINE_PATH=/var/lib/rizoma/agent/quarantine
RIZOMA_AGENT_AV_AUTO_QUARANTINE=false
RIZOMA_AGENT_AV_RESOURCE_AWARE=true
EOF

if command -v systemctl >/dev/null 2>&1; then
  run_root systemctl daemon-reload
  run_root systemctl reset-failed rizoma-agent >/dev/null 2>&1 || true
  if [[ "$FIREWALL_MODE" == "nftables" ]]; then
    run_root systemctl enable --now nftables || true
    run_root systemctl enable --now fail2ban || true
  fi
  if [[ "$ENABLE_ANTIVIRUS" == "true" ]]; then
    run_root systemctl enable --now clamav-freshclam 2>/dev/null || true
    run_root systemctl enable --now clamav-daemon 2>/dev/null || true
    run_root systemctl enable --now clamd@scan 2>/dev/null || true
  fi
  run_root systemctl enable rizoma-agent >/dev/null 2>&1 || true
  run_root systemctl restart rizoma-agent
fi

# Wait for enrollment
echo "Waiting for agent enrollment..."
identity="$IDENTITY_PATH"
enrolled=false
for i in $(seq 1 30); do
  if run_root test -f "$identity"; then
    if command -v systemctl >/dev/null 2>&1; then
      if run_root systemctl is-active --quiet rizoma-agent; then
        enrolled=true
        break
      fi
    else
      enrolled=true
      break
    fi
  fi
  sleep 2
done

if [ "$enrolled" = true ]; then
  node_id="$(run_root cat "$identity" 2>/dev/null | jq -r '.id' 2>/dev/null || echo "")"
  mesh_ip="$(run_root cat "$identity" 2>/dev/null | jq -r '.mesh_ip' 2>/dev/null || echo "")"
  echo ""
  echo "  Agent enrolled successfully!"
  echo "  Node ID:  $node_id"
  echo "  Mesh IP:  $mesh_ip"
  echo "  Name:     $NODE_NAME"
  echo "  Profile:  $PROFILE"
  echo "  Runtime:  $RUNTIME_MODE"
  echo ""
  echo "  Coordinator: $COORDINATOR_URL"
  echo "  Updates: $UPDATE_COMMAND"
  echo ""
else
  echo ""
  echo "  Agent enrollment failed or timed out."
  echo "  Check logs: journalctl -u rizoma-agent --no-pager -n 20"
  echo ""
  run_root journalctl -u rizoma-agent --no-pager -n 5 2>/dev/null || true
fi
