#!/usr/bin/env bash
#
# Malairte Node Installer for Linux and macOS.
#
# Usage:
#   curl -fsSL https://explorer.malairtebitcoin.com/downloads/malairte-installer.sh | sudo bash
#   curl -fsSL https://explorer.malairtebitcoin.com/downloads/malairte-installer.sh -o install.sh && sudo bash install.sh --key=<64-hex-key>
#
# Flags:
#   --key=<64-hex-chars>   Miner private key. If omitted, prompts (or reuses existing).
#                          Also used to authenticate heartbeats to the dashboard —
#                          the node posts its pubkey (derived from this key) with
#                          each heartbeat; no separate token is needed.
#   --worker=<name>        Worker label shown on the dashboard (default: hostname).
#   --threads=<N>          CPU mining threads (default: half of logical cores).
#   --gpu                  Enable GPU mining (falls back to CPU-only if no device).
#   --no-service           Install binaries only; don't create a systemd/launchd unit.
#   --uninstall            Remove the service and binaries.
#

set -euo pipefail

BASE_URL='https://explorer.malairtebitcoin.com/downloads'
SEED='104.192.5.197:9333'
SVC_NAME='malairte-node'
HEARTBEAT_URL='https://explorer.malairtebitcoin.com/api/v1/miner/heartbeat'

# ── Color output ─────────────────────────────────────────────────────────────
if [ -t 1 ]; then
    C_AMBER='\033[38;5;214m'; C_GREEN='\033[1;32m'; C_RED='\033[1;31m'
    C_DIM='\033[2m'; C_BOLD='\033[1m'; C_RESET='\033[0m'
else
    C_AMBER=''; C_GREEN=''; C_RED=''; C_DIM=''; C_BOLD=''; C_RESET=''
fi

say()  { printf "${C_AMBER}»${C_RESET} %s\n" "$*"; }
ok()   { printf "${C_GREEN}✓${C_RESET} %s\n" "$*"; }
warn() { printf "${C_RED}!${C_RESET} %s\n" "$*" >&2; }
die()  { warn "$*"; exit 1; }

# ── Parse flags ──────────────────────────────────────────────────────────────
MINER_KEY=''
WORKER_ID=''
THREADS=''
GPU_FLAG=''
INSTALL_SERVICE=1
UNINSTALL=0
for arg in "$@"; do
    case "$arg" in
        --key=*)       MINER_KEY="${arg#*=}" ;;
        --worker=*)    WORKER_ID="${arg#*=}" ;;
        --threads=*)   THREADS="${arg#*=}" ;;
        --gpu)         GPU_FLAG='--gpu' ;;
        --no-service)  INSTALL_SERVICE=0 ;;
        --token=*)     warn "--token is deprecated and ignored (the node now authenticates with its pubkey)" ;;
        --uninstall)   UNINSTALL=1 ;;
        *)             die "Unknown flag: $arg" ;;
    esac
done

# ── Detect OS and arch ───────────────────────────────────────────────────────
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
case "$OS" in
    linux)  OS_NAME='linux'  ;;
    darwin) OS_NAME='darwin' ;;
    *)      die "Unsupported OS: $OS (this installer handles Linux and macOS only)" ;;
esac

ARCH="$(uname -m)"
case "$ARCH" in
    x86_64|amd64) ARCH_NAME='amd64' ;;
    arm64|aarch64) ARCH_NAME='arm64' ;;
    *)            die "Unsupported arch: $ARCH" ;;
esac

NODE_BIN="malairte-node-${OS_NAME}-${ARCH_NAME}"
CLI_BIN="malairte-cli-${OS_NAME}-${ARCH_NAME}"

# When a GPU is present and the user enabled --gpu, prefer the CUDA variant.
# Only available for linux/amd64 right now; other platforms fall through to CPU binary.
if [ -n "$GPU_FLAG" ] && [ "$OS_NAME" = 'linux' ] && [ "$ARCH_NAME" = 'amd64' ]; then
    NODE_BIN="malairte-node-linux-amd64-cuda"
fi

# Platform-specific paths
if [ "$OS_NAME" = 'darwin' ]; then
    BIN_DIR='/usr/local/bin'
    DATA_DIR="$HOME/Library/Application Support/Malairted"
    LOG_DIR="$HOME/Library/Logs/Malairted"
    LAUNCH_PLIST="$HOME/Library/LaunchAgents/com.malairte.node.plist"
else
    BIN_DIR='/usr/local/bin'
    DATA_DIR='/var/lib/malairte-node'
    LOG_DIR='/var/log/malairte-node'
    SYSTEMD_UNIT='/etc/systemd/system/malairte-node.service'
fi

# ── Uninstall path ───────────────────────────────────────────────────────────
if [ "$UNINSTALL" = '1' ]; then
    say "Uninstalling Malairte node..."
    if [ "$OS_NAME" = 'linux' ]; then
        if [ -f "$SYSTEMD_UNIT" ]; then
            systemctl stop "$SVC_NAME" 2>/dev/null || true
            systemctl disable "$SVC_NAME" 2>/dev/null || true
            rm -f "$SYSTEMD_UNIT"
            systemctl daemon-reload
        fi
    else
        if [ -f "$LAUNCH_PLIST" ]; then
            launchctl unload "$LAUNCH_PLIST" 2>/dev/null || true
            rm -f "$LAUNCH_PLIST"
        fi
    fi
    rm -f "$BIN_DIR/malairte-node" "$BIN_DIR/malairte-cli"
    ok "Removed binaries and service (data in $DATA_DIR kept — delete manually if desired)"
    exit 0
fi

# ── Detect existing installation ─────────────────────────────────────────────
EXISTING_KEY=''
EXISTING_WORKER=''
SERVICE_RUNNING=0
if [ "$OS_NAME" = 'linux' ] && [ -f "$SYSTEMD_UNIT" ]; then
    EXISTING_KEY="$(grep -oE -- '--miner-key=[0-9a-fA-F]{64}' "$SYSTEMD_UNIT" | head -1 | cut -d= -f2 || true)"
    EXISTING_WORKER="$(grep -oE -- '--heartbeat-worker=[^ ]+' "$SYSTEMD_UNIT" | head -1 | cut -d= -f2 || true)"
    if systemctl is-active --quiet "$SVC_NAME" 2>/dev/null; then SERVICE_RUNNING=1; fi
elif [ "$OS_NAME" = 'darwin' ] && [ -f "$LAUNCH_PLIST" ]; then
    EXISTING_KEY="$(grep -oE -- '--miner-key=[0-9a-fA-F]{64}' "$LAUNCH_PLIST" | head -1 | cut -d= -f2 || true)"
    EXISTING_WORKER="$(grep -oE -- '--heartbeat-worker=[^<]+' "$LAUNCH_PLIST" | head -1 | cut -d= -f2 || true)"
    if launchctl list 2>/dev/null | grep -q 'com.malairte.node'; then SERVICE_RUNNING=1; fi
fi

# ── GPU detection ────────────────────────────────────────────────────────────
GPU_INFO=''
if [ "$OS_NAME" = 'linux' ]; then
    if command -v lspci >/dev/null 2>&1; then
        GPU_INFO="$(lspci 2>/dev/null | grep -iE 'vga|3d|display' | grep -viE 'microsoft|virtual|qxl|cirrus|basic display' | head -1 | cut -d: -f3- | sed 's/^ *//' || true)"
    fi
    if [ -z "$GPU_INFO" ] && command -v nvidia-smi >/dev/null 2>&1; then
        GPU_INFO="$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 || true)"
    fi
else
    if command -v system_profiler >/dev/null 2>&1; then
        GPU_INFO="$(system_profiler SPDisplaysDataType 2>/dev/null | awk -F': ' '/Chipset Model/ {print $2; exit}' || true)"
    fi
fi

# ── Banner ───────────────────────────────────────────────────────────────────
printf "\n${C_AMBER}${C_BOLD}Malairte Node Installer${C_RESET}\n"
printf "${C_DIM}─────────────────────────────${C_RESET}\n"
printf "  OS:       %s/%s\n" "$OS_NAME" "$ARCH_NAME"
printf "  Bin dir:  %s\n" "$BIN_DIR"
printf "  Data dir: %s\n" "$DATA_DIR"
if [ -n "$GPU_INFO" ]; then
    printf "  GPU:      ${C_GREEN}%s${C_RESET}\n" "$GPU_INFO"
else
    printf "  GPU:      ${C_DIM}no discrete GPU detected — CPU mining only${C_RESET}\n"
fi
if [ -n "$EXISTING_KEY" ]; then
    printf "  Status:   ${C_GREEN}existing installation detected${C_RESET} — key will be reused\n"
fi
printf "\n"

# ── Privilege check (Linux needs root, macOS user-level launchd is fine) ─────
if [ "$OS_NAME" = 'linux' ] && [ "$(id -u)" != '0' ]; then
    die "Linux install needs root. Re-run with: sudo bash $0 $*"
fi

# ── Resolve miner key ────────────────────────────────────────────────────────
if [ -z "$MINER_KEY" ] && [ -n "$EXISTING_KEY" ]; then
    MINER_KEY="$EXISTING_KEY"
    ok "Reusing existing miner key from previous installation"
fi

if [ -z "$MINER_KEY" ]; then
    if [ -t 0 ]; then
        printf "${C_AMBER}Enter your 64-char hex miner private key${C_RESET}\n"
        printf "${C_DIM}(generate one at https://explorer.malairtebitcoin.com/onboarding):${C_RESET} "
        read -r MINER_KEY
    else
        die "No miner key provided. Re-run with --key=<64-hex-chars> or run interactively."
    fi
fi

if ! printf '%s' "$MINER_KEY" | grep -Eq '^[0-9a-fA-F]{64}$'; then
    die "Invalid miner key — must be exactly 64 hex characters."
fi

# ── Resolve worker label ─────────────────────────────────────────────────────
if [ -z "$WORKER_ID" ] && [ -n "$EXISTING_WORKER" ]; then
    WORKER_ID="$EXISTING_WORKER"
fi
if [ -z "$WORKER_ID" ]; then
    WORKER_ID="$(hostname 2>/dev/null | tr -d ' ' | cut -c1-32 || echo 'worker-1')"
fi

# ── Resolve threads ──────────────────────────────────────────────────────────
if [ -z "$THREADS" ]; then
    if [ "$OS_NAME" = 'darwin' ]; then
        CORES="$(sysctl -n hw.logicalcpu 2>/dev/null || echo 4)"
    else
        CORES="$(nproc 2>/dev/null || echo 4)"
    fi
    THREADS="$(( CORES / 2 ))"
    [ "$THREADS" -lt 1 ] && THREADS=1
fi

# ── Download binaries ────────────────────────────────────────────────────────
say "Downloading binaries for ${OS_NAME}/${ARCH_NAME}..."
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

curl -fsSL "$BASE_URL/$NODE_BIN" -o "$TMP/malairte-node" \
    || die "Failed to download $BASE_URL/$NODE_BIN"
curl -fsSL "$BASE_URL/$CLI_BIN" -o "$TMP/malairte-cli" \
    || die "Failed to download $BASE_URL/$CLI_BIN"

chmod +x "$TMP/malairte-node" "$TMP/malairte-cli"
ok "Downloaded"

# ── Stop existing service before overwriting binary ──────────────────────────
if [ "$SERVICE_RUNNING" = '1' ]; then
    say "Stopping running service..."
    if [ "$OS_NAME" = 'linux' ]; then
        systemctl stop "$SVC_NAME"
    else
        launchctl unload "$LAUNCH_PLIST" 2>/dev/null || true
    fi
fi

# ── Install binaries ─────────────────────────────────────────────────────────
say "Installing to $BIN_DIR..."
mkdir -p "$BIN_DIR" "$DATA_DIR" "$LOG_DIR"
install -m 0755 "$TMP/malairte-node"  "$BIN_DIR/malairte-node"
install -m 0755 "$TMP/malairte-cli" "$BIN_DIR/malairte-cli"
ok "Binaries installed"

# ── Create service ───────────────────────────────────────────────────────────
if [ "$INSTALL_SERVICE" = '1' ]; then
    HEARTBEAT_ARGS="--heartbeat-url=$HEARTBEAT_URL --heartbeat-worker=$WORKER_ID"
    ARGS="--data-dir=$DATA_DIR --network=mainnet --rpc-addr=127.0.0.1:9332 --mine --miner-key=$MINER_KEY --mine-threads=$THREADS $GPU_FLAG --seeds=$SEED $HEARTBEAT_ARGS"

    if [ "$OS_NAME" = 'linux' ]; then
        say "Creating systemd service..."
        cat >"$SYSTEMD_UNIT" <<UNIT
[Unit]
Description=Malairt Blockchain Node
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=$BIN_DIR/malairte-node $ARGS
Restart=always
RestartSec=5
StandardOutput=append:$LOG_DIR/malairte-node.log
StandardError=append:$LOG_DIR/malairte-node.log
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
UNIT
        systemctl daemon-reload
        systemctl enable "$SVC_NAME" >/dev/null
        systemctl start  "$SVC_NAME"
        sleep 2
        if systemctl is-active --quiet "$SVC_NAME"; then
            ok "Service running — check logs with: journalctl -u $SVC_NAME -f"
        else
            warn "Service failed to start — check: systemctl status $SVC_NAME"
        fi
    else
        say "Creating launchd agent..."
        mkdir -p "$(dirname "$LAUNCH_PLIST")"
        cat >"$LAUNCH_PLIST" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>                <string>com.malairte.node</string>
    <key>ProgramArguments</key>
    <array>
        <string>$BIN_DIR/malairte-node</string>
        <string>--data-dir=$DATA_DIR</string>
        <string>--network=mainnet</string>
        <string>--rpc-addr=127.0.0.1:9332</string>
        <string>--mine</string>
        <string>--miner-key=$MINER_KEY</string>
        <string>--mine-threads=$THREADS</string>$( [ -n "$GPU_FLAG" ] && printf '\n        <string>%s</string>' "$GPU_FLAG" )
        <string>--seeds=$SEED</string>
        <string>--heartbeat-url=$HEARTBEAT_URL</string>
        <string>--heartbeat-worker=$WORKER_ID</string>
    </array>
    <key>RunAtLoad</key>            <true/>
    <key>KeepAlive</key>            <true/>
    <key>StandardOutPath</key>      <string>$LOG_DIR/malairte-node.log</string>
    <key>StandardErrorPath</key>    <string>$LOG_DIR/malairte-node.log</string>
</dict>
</plist>
PLIST
        launchctl load "$LAUNCH_PLIST"
        sleep 2
        if launchctl list | grep -q 'com.malairte.node'; then
            ok "launchd agent running — check logs with: tail -f $LOG_DIR/malairte-node.log"
        else
            warn "launchd agent failed to start — check: tail $LOG_DIR/malairte-node.log"
        fi
    fi
fi

# ── Post-install summary ─────────────────────────────────────────────────────
printf "\n${C_GREEN}${C_BOLD}✓ Installation complete${C_RESET}\n\n"
printf "  Node binary:   %s\n" "$BIN_DIR/malairte-node"
printf "  CLI binary:    %s\n" "$BIN_DIR/malairte-cli"
printf "  Data dir:      %s\n" "$DATA_DIR"
printf "  Log file:      %s/malairte-node.log\n" "$LOG_DIR"
if [ -n "$GPU_FLAG" ]; then
    printf "  GPU mining:    ${C_GREEN}enabled${C_RESET} (falls back to CPU if OpenCL unavailable)\n"
fi
printf "  Dashboard:     ${C_GREEN}heartbeats enabled${C_RESET} (worker=${C_BOLD}%s${C_RESET})\n" "$WORKER_ID"
printf "\nNext steps:\n"
printf "  • Check balance at ${C_AMBER}https://explorer.malairtebitcoin.com${C_RESET}\n"
if [ "$INSTALL_SERVICE" = '1' ] && [ "$OS_NAME" = 'linux' ]; then
    printf "  • Watch logs:  journalctl -u %s -f\n" "$SVC_NAME"
    printf "  • Stop/start:  systemctl stop|start %s\n" "$SVC_NAME"
elif [ "$INSTALL_SERVICE" = '1' ]; then
    printf "  • Watch logs:  tail -f %s/malairte-node.log\n" "$LOG_DIR"
    printf "  • Stop:        launchctl unload %s\n" "$LAUNCH_PLIST"
fi
printf "  • Open P2P port 9333 in your firewall for peer connections\n\n"
