#!/usr/bin/env bash
#
# Paritr Node - installer for Linux (Ubuntu / Debian / Raspberry Pi OS)
# ---------------------------------------------------------------------
# Installs a full Paritr node as a systemd service.
# Supported architectures: x86_64 (amd64), aarch64 (arm64), armv7l (armhf)
#
#   ./install.sh                                  # interactive
#   ./install.sh --address P... --port 5050       # unattended
#   ./install.sh --cores 2 --intensity 60         # limit hardware usage
#
set -euo pipefail

NODE_USER="$(whoami)"
NODE_DIR="${PARITR_DIR:-$HOME/paritr-node}"
SERVICE_NAME="paritr-node"
RPC_PORT=5050
MINER_ADDRESS=""
PUBLIC_URL=""
MINING_CORES=0
MINING_INTENSITY=100
CPU_QUOTA=""
SOURCE_BASE="${PARITR_SOURCE:-}"
ASSUME_YES=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --address)     MINER_ADDRESS="$2"; shift 2 ;;
    --port)        RPC_PORT="$2"; shift 2 ;;
    --public-url)  PUBLIC_URL="$2"; shift 2 ;;
    --dir)         NODE_DIR="$2"; shift 2 ;;
    --source)      SOURCE_BASE="$2"; shift 2 ;;
    --cores)       MINING_CORES="$2"; shift 2 ;;
    --intensity)   MINING_INTENSITY="$2"; shift 2 ;;
    --cpu-quota)   CPU_QUOTA="$2"; shift 2 ;;
    -y|--yes)      ASSUME_YES=1; shift ;;
    -h|--help)
      cat <<'USAGE'
Usage: ./install.sh [options]

  --address <wallet>    Payout address for mining rewards
  --port <port>         RPC port (default 5050)
  --public-url <url>    Publicly reachable address of this node
                        (detected automatically when omitted)
  --dir <path>          Installation directory (default ~/paritr-node)
  --source <url>        Base URL to download node.py from
  --cores <n>           Mining worker processes (0 = automatic)
  --intensity <5-100>   Load per worker process in percent
  --cpu-quota <n%>      Hard systemd limit, e.g. 150% = 1.5 cores
  -y, --yes             Accept all detected defaults without asking
USAGE
      exit 0 ;;
    *) echo "Unknown option: $1"; exit 1 ;;
  esac
done

ARCH="$(uname -m)"
case "$ARCH" in
  x86_64|amd64)   ARCH_LABEL="x86_64 (amd64)" ;;
  aarch64|arm64)  ARCH_LABEL="ARM 64-bit (aarch64)" ;;
  armv7l|armv6l)  ARCH_LABEL="ARM 32-bit ($ARCH)" ;;
  *)              ARCH_LABEL="$ARCH (untested)" ;;
esac

echo "=============================================================="
echo "   Paritr Full Node - installation"
echo "   Architecture : $ARCH_LABEL"
echo "   CPU cores    : $(nproc 2>/dev/null || echo '?')"
echo "=============================================================="

if [[ -z "$MINER_ADDRESS" && -t 0 && $ASSUME_YES -eq 0 ]]; then
  read -rp "Wallet address for mining rewards (empty = set later): " MINER_ADDRESS
fi

echo "[1/7] Installing system packages ..."
if command -v apt-get >/dev/null 2>&1; then
  sudo apt-get update -qq
  # python3-dev/build-essential are needed on ARM when a wheel is missing
  sudo apt-get install -y -qq python3 python3-venv python3-pip python3-dev \
    build-essential curl ca-certificates
  sudo apt-get install -y -qq ufw >/dev/null 2>&1 || true
elif command -v dnf >/dev/null 2>&1; then
  sudo dnf install -y python3 python3-pip python3-devel gcc curl
elif command -v pacman >/dev/null 2>&1; then
  sudo pacman -Sy --noconfirm python python-pip base-devel curl
else
  echo "      Unknown package manager - please install Python 3.9+ yourself."
fi
echo "      Python $(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])') detected."

echo "[2/7] Preparing node directory: $NODE_DIR"
mkdir -p "$NODE_DIR"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "$SCRIPT_DIR/../node.py" ]]; then
  cp "$SCRIPT_DIR/../node.py" "$SCRIPT_DIR/../wsgi.py" "$NODE_DIR/"
  cp "$SCRIPT_DIR/manage.sh" "$NODE_DIR/" 2>/dev/null || true
elif [[ -f "$SCRIPT_DIR/node.py" ]]; then
  cp "$SCRIPT_DIR/node.py" "$SCRIPT_DIR/wsgi.py" "$NODE_DIR/"
  cp "$SCRIPT_DIR/manage.sh" "$NODE_DIR/" 2>/dev/null || true
elif [[ -n "$SOURCE_BASE" ]]; then
  echo "      Downloading node software from $SOURCE_BASE ..."
  curl -fsSL "$SOURCE_BASE/node.py" -o "$NODE_DIR/node.py"
  curl -fsSL "$SOURCE_BASE/wsgi.py" -o "$NODE_DIR/wsgi.py"
  # The management helper belongs next to the node, otherwise a remotely
  # installed node cannot be administered locally at all.
  curl -fsSL "$SOURCE_BASE/manage.sh" -o "$NODE_DIR/manage.sh"
else
  echo "ERROR: node.py not found. Please pass --source <url>."
  exit 1
fi
chmod +x "$NODE_DIR/manage.sh" 2>/dev/null || true

echo "[3/7] Setting up the Python environment and dependencies ..."
python3 -m venv "$NODE_DIR/venv"
"$NODE_DIR/venv/bin/pip" install --quiet --upgrade pip wheel
"$NODE_DIR/venv/bin/pip" install --quiet Flask requests ecdsa gunicorn

echo "[4/7] Determining the public address ..."
# Other nodes need a way back to this machine. Behind a router it cannot know
# its own public address, so a seed node is asked what it sees. The value is
# only ever a suggestion - the operator confirms or overrides it.
if [[ -z "$PUBLIC_URL" ]]; then
  DETECTED="$("$NODE_DIR/venv/bin/python" "$NODE_DIR/node.py" \
    --port "$RPC_PORT" --detect-public-url 2>/dev/null || true)"
  if [[ -n "$DETECTED" ]]; then
    echo "      Detected: $DETECTED"
    if [[ -t 0 && $ASSUME_YES -eq 0 ]]; then
      read -rp "      Public address of this node [$DETECTED]: " PUBLIC_URL
      PUBLIC_URL="${PUBLIC_URL:-$DETECTED}"
    else
      PUBLIC_URL="$DETECTED"
    fi
  else
    echo "      Could not be detected (no internet access or seed unreachable)."
    if [[ -t 0 && $ASSUME_YES -eq 0 ]]; then
      read -rp "      Public address of this node (empty = none): " PUBLIC_URL
    fi
  fi
fi
if [[ -n "$PUBLIC_URL" ]]; then
  echo "      Using: $PUBLIC_URL"
  echo "      Remember to forward TCP port $RPC_PORT to this machine."
else
  echo "      No public address set - this node will only dial out."
fi

echo "[5/7] Writing the configuration ..."
if [[ ! -f "$NODE_DIR/config.json" ]]; then
  ADMIN_SECRET="$(python3 -c 'import secrets; print(secrets.token_hex(24))')"
  PUBLIC_URL_JSON="$(python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$PUBLIC_URL")"
  MINER_ADDRESS_JSON="$(python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$MINER_ADDRESS")"
  cat > "$NODE_DIR/config.json" <<EOF
{
  "network": "paritr-mainnet",
  "rpc_host": "0.0.0.0",
  "rpc_port": $RPC_PORT,
  "public_url": $PUBLIC_URL_JSON,
  "admin_secret": "$ADMIN_SECRET",
  "miner_address": $MINER_ADDRESS_JSON,
  "mining_enabled": true,
  "mining_processes": $MINING_CORES,
  "mining_intensity": $MINING_INTENSITY,
  "seed_nodes": ["https://node0.oe-net.de", "http://node0.oe-net.de:5050"],
  "peers": [],
  "data_dir": "data",
  "rpc_cors_origins": "*",
  "log_level": "INFO"
}
EOF
  chmod 600 "$NODE_DIR/config.json"
else
  echo "      Existing config.json left untouched."
fi

echo "[6/7] Installing the systemd service ..."
sudo tee "/etc/systemd/system/$SERVICE_NAME.service" > /dev/null <<EOF
[Unit]
Description=Paritr Full Node
Documentation=https://node0.oe-net.de
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=$NODE_USER
WorkingDirectory=$NODE_DIR
ExecStart=$NODE_DIR/venv/bin/gunicorn -k gthread -w 1 --threads 8 --timeout 120 -b 0.0.0.0:$RPC_PORT wsgi:app
Restart=always
RestartSec=5
KillSignal=SIGINT
StandardOutput=journal
StandardError=journal
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=read-only
ReadWritePaths=$NODE_DIR
# Hardware budget: mining runs at low priority so the server stays usable.
# CPUQuota can be adjusted at any time via "manage.sh cpuquota <n%>".
Nice=10
CPUWeight=20
IOWeight=20
${CPU_QUOTA:+CPUQuota=$CPU_QUOTA}

[Install]
WantedBy=multi-user.target
EOF

echo "[7/7] Opening the firewall and starting the service ..."
if command -v ufw >/dev/null 2>&1; then
  sudo ufw allow "$RPC_PORT/tcp" >/dev/null 2>&1 || true
elif command -v firewall-cmd >/dev/null 2>&1; then
  sudo firewall-cmd --permanent --add-port="$RPC_PORT/tcp" >/dev/null 2>&1 || true
  sudo firewall-cmd --reload >/dev/null 2>&1 || true
fi
sudo systemctl daemon-reload
sudo systemctl enable --now "$SERVICE_NAME.service"
sleep 3

echo
echo "=============================================================="
sudo systemctl --no-pager --lines=0 status "$SERVICE_NAME.service" || true
echo "=============================================================="
echo " Node directory   : $NODE_DIR"
echo " Architecture     : $ARCH_LABEL"
echo " RPC endpoint     : http://$(hostname -I | awk '{print $1}'):$RPC_PORT"
echo " Public address   : ${PUBLIC_URL:-<none>}"
echo " Admin secret key : $(python3 -c "import json;print(json.load(open('$NODE_DIR/config.json'))['admin_secret'])")"
echo
echo " You need this secret key to connect the node in ParitrWallet."
echo
echo " Show logs        : journalctl -u $SERVICE_NAME -f"
echo " Restart service  : sudo systemctl restart $SERVICE_NAME"
echo " Manage the node  : cd $NODE_DIR && ./manage.sh status"
echo "=============================================================="
