#!/usr/bin/env sh
# Blackhole — universal one-click installer
# Supports: Linux (systemd, OpenRC, runit, OpenWrt), macOS (launchd), manual fallback
# Usage: curl -fsSL https://blackholemesh.dev/install.sh | sh
set -e

REPO="jjarjji/blackhole"
INSTALL_DIR="/usr/local/bin"
BINARY_NAME="blackhole"
HUB_URL="${BH_HUB_URL:-https://209.145.61.184:8090}"
CONFIG_DIR="${BH_CONFIG_DIR:-/etc/blackhole}"
SERVICE_LABEL="dev.blackholemesh.agent"
SERVICE_NAME="blackhole-agent"

# ── Logging ───────────────────────────────────────────────────────────────────
log()  { printf '\033[0;36m[blackhole]\033[0m %s\n' "$1"; }
warn() { printf '\033[0;33m[warn]\033[0m %s\n' "$1" >&2; }
err()  { printf '\033[0;31m[error]\033[0m %s\n' "$1" >&2; exit 1; }

# ── Platform detection ────────────────────────────────────────────────────────
detect_platform() {
    OS=$(uname -s | tr '[:upper:]' '[:lower:]')
    ARCH=$(uname -m)

    case "$ARCH" in
        x86_64)        ARCH="amd64"  ;;
        aarch64|arm64) ARCH="arm64"  ;;
        armv7l)        ARCH="armv7"  ;;
        armv6l)        ARCH="armv6"  ;;
        mips)          ARCH="mips"   ;;
        mips64)        ARCH="mips64" ;;
        *) err "Unsupported architecture: $ARCH" ;;
    esac

    case "$OS" in
        linux)  BINARY_SUFFIX="linux-${ARCH}"  ;;
        darwin) BINARY_SUFFIX="darwin-${ARCH}" ;;
        *) err "Unsupported OS: $OS (use scripts/install-windows.ps1 on Windows)" ;;
    esac

    log "Detected platform: $OS/$ARCH"
}

# ── Service manager detection ─────────────────────────────────────────────────
detect_service_manager() {
    if [ -f /etc/openwrt_release ]; then
        SERVICE_MGR="openwrt"
    elif uname -s | grep -q Darwin; then
        SERVICE_MGR="launchd"
    elif command -v systemctl >/dev/null 2>&1 && systemctl is-system-running >/dev/null 2>&1; then
        SERVICE_MGR="systemd"
    elif [ -f /sbin/openrc-run ] || command -v rc-service >/dev/null 2>&1; then
        SERVICE_MGR="openrc"
    elif [ -d /etc/sv ]; then
        SERVICE_MGR="runit"
    else
        SERVICE_MGR="none"
        warn "No recognized service manager — binary installed but not auto-started"
    fi

    log "Service manager: $SERVICE_MGR"
}

# ── Download with retry ───────────────────────────────────────────────────────
download_with_retry() {
    local url="$1" dest="$2" attempt=1
    while [ $attempt -le 3 ]; do
        if curl -fsSL "$url" -o "$dest" 2>/dev/null; then
            return 0
        fi
        warn "Download attempt $attempt failed, retrying..."
        attempt=$((attempt + 1))
        sleep $((attempt * 2))
    done
    err "Failed to download after 3 attempts: $url"
}

# ── Resolve artifact name ─────────────────────────────────────────────────────
resolve_artifact() {
    case "${OS}-${ARCH}" in
        linux-amd64)   ARTIFACT="blackhole-linux-amd64" ;;
        linux-arm64)   ARTIFACT="blackhole-linux-arm64-nanoclaw" ;;
        linux-armv7)   ARTIFACT="blackhole-linux-armv7-nanoclaw" ;;
        linux-armv6)   ARTIFACT="blackhole-linux-armv6-nanoclaw" ;;
        linux-mips)    ARTIFACT="blackhole-linux-mips-picoclaw" ;;
        linux-mips64)  ARTIFACT="blackhole-linux-mips64-picoclaw" ;;
        darwin-amd64)  ARTIFACT="blackhole-darwin-amd64" ;;
        darwin-arm64)  ARTIFACT="blackhole-darwin-arm64" ;;
        *) err "Unsupported platform: $OS/$ARCH" ;;
    esac
}

# ── Install binary ────────────────────────────────────────────────────────────
install_binary() {
    log "Fetching latest release tag..."
    LATEST=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" \
        | grep '"tag_name"' | cut -d'"' -f4)
    [ -z "$LATEST" ] && err "Could not fetch latest release. Check: https://github.com/$REPO/releases"

    DOWNLOAD_URL="https://github.com/$REPO/releases/download/$LATEST/${ARTIFACT}"
    log "Downloading $LATEST ($ARTIFACT)..."
    download_with_retry "$DOWNLOAD_URL" /tmp/blackhole-bin
    chmod +x /tmp/blackhole-bin

    if [ "$(id -u)" = "0" ]; then
        mv /tmp/blackhole-bin "$INSTALL_DIR/$BINARY_NAME"
    else
        sudo mv /tmp/blackhole-bin "$INSTALL_DIR/$BINARY_NAME"
    fi
    log "Binary installed: $INSTALL_DIR/$BINARY_NAME"

    # Download bhwatch watchdog (optional — warn and continue if not yet in releases)
    BHWATCH_URL="https://github.com/$REPO/releases/download/$LATEST/bhwatch-${BINARY_SUFFIX}"
    if download_with_retry "$BHWATCH_URL" "/tmp/bhwatch" 2>/dev/null; then
        if [ "$(id -u)" = "0" ]; then
            install -m 755 /tmp/bhwatch "${INSTALL_DIR}/bhwatch"
        else
            sudo install -m 755 /tmp/bhwatch "${INSTALL_DIR}/bhwatch"
        fi
        log "bhwatch installed ✓"
    else
        warn "bhwatch not available for this platform — service will run without C watchdog layer"
    fi
}

# ── Verify SHA256 checksum ────────────────────────────────────────────────────
verify_checksum() {
    local binary="$1"
    local expected_url="https://github.com/jjarjji/blackhole/releases/download/${LATEST}/SHA256SUMS"
    local checksums
    checksums=$(curl -fsSL "$expected_url" 2>/dev/null) || { warn "Could not fetch checksums — skipping verification"; return 0; }
    local expected
    expected=$(echo "$checksums" | grep "$(basename "$binary")" | awk '{print $1}')
    if [ -z "$expected" ]; then
        warn "No checksum entry for $(basename "$binary") — skipping"
        return 0
    fi
    local got
    got=$(sha256sum "$binary" 2>/dev/null | awk '{print $1}' || shasum -a 256 "$binary" 2>/dev/null | awk '{print $1}')
    if [ "$got" != "$expected" ]; then
        err "Checksum mismatch: got $got want $expected"
        exit 1
    fi
    log "Checksum verified ✓"
}

# ── Verify installation ───────────────────────────────────────────────────────
verify_install() {
    if "$INSTALL_DIR/$BINARY_NAME" --version >/dev/null 2>&1; then
        log "Install verified"
    else
        warn "Binary installed but failed to run — check that the binary is compatible with this system"
    fi
}

# ── systemd ───────────────────────────────────────────────────────────────────
install_systemd() {
    log "Installing systemd service..."

    # Canonical env vars — all required for healerd cross-signed ops channel.
    # BH_NATS_URL: NATS connection URL (use 127.0.0.1 for local hub, or the hub IP for leaf nodes)
    # BH_MACHINE: machine hostname used in bh.ops.exec.{machine} subject routing
    # HOME: required so healerd can find ~/.config/blackhole/ for signing keys
    cat > /tmp/${SERVICE_NAME}.service << EOF
[Unit]
Description=Blackhole Mesh Agent
Documentation=https://blackholemesh.dev/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
Environment="BH_NATS_URL=${BH_NATS_URL:-nats://127.0.0.1:4222}"
Environment="BH_MACHINE=${BH_MACHINE:-$(hostname)}"
Environment="HOME=${HOME:-/root}"
ExecStart=$INSTALL_DIR/$BINARY_NAME daemon start --hub $HUB_URL --config $CONFIG_DIR
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW

[Install]
WantedBy=multi-user.target
EOF
    if [ "$(id -u)" = "0" ]; then
        mv /tmp/${SERVICE_NAME}.service /etc/systemd/system/${SERVICE_NAME}.service
        systemctl daemon-reload
        systemctl enable "$SERVICE_NAME"
        systemctl restart "$SERVICE_NAME" || true
    else
        sudo mv /tmp/${SERVICE_NAME}.service /etc/systemd/system/${SERVICE_NAME}.service
        sudo systemctl daemon-reload
        sudo systemctl enable "$SERVICE_NAME"
        sudo systemctl restart "$SERVICE_NAME" || true
    fi
    log "systemd service enabled: $SERVICE_NAME"
}

# ── OpenRC ────────────────────────────────────────────────────────────────────
install_openrc() {
    log "Installing OpenRC init script..."
    cat > /tmp/${SERVICE_NAME} << EOF
#!/sbin/openrc-run
description="Blackhole Mesh Agent"
command="$INSTALL_DIR/$BINARY_NAME"
command_args="daemon start --hub $HUB_URL --config $CONFIG_DIR"
command_background=true
pidfile="/run/${SERVICE_NAME}.pid"
depend() {
    need net
    after firewall
}
EOF
    if [ "$(id -u)" = "0" ]; then
        mv /tmp/${SERVICE_NAME} /etc/init.d/${SERVICE_NAME}
        chmod +x /etc/init.d/${SERVICE_NAME}
        rc-update add "$SERVICE_NAME" default
        rc-service "$SERVICE_NAME" start || true
    else
        sudo mv /tmp/${SERVICE_NAME} /etc/init.d/${SERVICE_NAME}
        sudo chmod +x /etc/init.d/${SERVICE_NAME}
        sudo rc-update add "$SERVICE_NAME" default
        sudo rc-service "$SERVICE_NAME" start || true
    fi
    log "OpenRC service enabled: $SERVICE_NAME"
}

# ── runit ─────────────────────────────────────────────────────────────────────
install_runit() {
    log "Installing runit service..."
    SV_DIR="/etc/sv/$SERVICE_NAME"
    if [ "$(id -u)" = "0" ]; then
        mkdir -p "$SV_DIR"
        cat > "$SV_DIR/run" << EOF
#!/bin/sh
exec $INSTALL_DIR/$BINARY_NAME daemon start --hub $HUB_URL --config $CONFIG_DIR
EOF
        chmod +x "$SV_DIR/run"
        ln -sf "$SV_DIR" /var/service/ 2>/dev/null || true
    else
        sudo mkdir -p "$SV_DIR"
        sudo sh -c "cat > '$SV_DIR/run' << 'RUNEOF'
#!/bin/sh
exec $INSTALL_DIR/$BINARY_NAME daemon start --hub $HUB_URL --config $CONFIG_DIR
RUNEOF"
        sudo chmod +x "$SV_DIR/run"
        sudo ln -sf "$SV_DIR" /var/service/ 2>/dev/null || true
    fi
    log "runit service installed: $SV_DIR"
}

# ── OpenWrt (procd) ───────────────────────────────────────────────────────────
install_openwrt() {
    log "Installing OpenWrt procd init script..."
    cat > /tmp/${SERVICE_NAME} << EOF
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=95
STOP=10
start_service() {
    procd_open_instance
    procd_set_param command $INSTALL_DIR/$BINARY_NAME daemon start --hub $HUB_URL --config $CONFIG_DIR
    procd_set_param respawn
    procd_close_instance
}
EOF
    mv /tmp/${SERVICE_NAME} /etc/init.d/${SERVICE_NAME}
    chmod +x /etc/init.d/${SERVICE_NAME}
    /etc/init.d/${SERVICE_NAME} enable
    /etc/init.d/${SERVICE_NAME} start || true
    log "OpenWrt procd service enabled: $SERVICE_NAME"
}

# ── launchd (macOS) ───────────────────────────────────────────────────────────
install_launchd() {
    log "Installing launchd LaunchDaemon (system-wide, survives logout)..."

    PLIST_DIR="/Library/LaunchDaemons"
    PLIST_PATH="$PLIST_DIR/${SERVICE_LABEL}.plist"

    cat > /tmp/${SERVICE_LABEL}.plist << EOF
<?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>${SERVICE_LABEL}</string>

    <key>ProgramArguments</key>
    <array>
        <string>$INSTALL_DIR/$BINARY_NAME</string>
        <string>daemon</string>
        <string>start</string>
        <string>--hub</string>
        <string>$HUB_URL</string>
        <string>--config</string>
        <string>$CONFIG_DIR</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <true/>

    <key>ThrottleInterval</key>
    <integer>5</integer>

    <key>StandardOutPath</key>
    <string>/var/log/blackhole-agent.log</string>

    <key>StandardErrorPath</key>
    <string>/var/log/blackhole-agent.log</string>
</dict>
</plist>
EOF

    if [ "$(id -u)" = "0" ]; then
        mv /tmp/${SERVICE_LABEL}.plist "$PLIST_PATH"
        chmod 644 "$PLIST_PATH"
        chown root:wheel "$PLIST_PATH"
        launchctl unload "$PLIST_PATH" 2>/dev/null || true
        launchctl load -w "$PLIST_PATH"
    else
        sudo mv /tmp/${SERVICE_LABEL}.plist "$PLIST_PATH"
        sudo chmod 644 "$PLIST_PATH"
        sudo chown root:wheel "$PLIST_PATH"
        sudo launchctl unload "$PLIST_PATH" 2>/dev/null || true
        sudo launchctl load -w "$PLIST_PATH"
    fi
    log "launchd LaunchDaemon loaded: $SERVICE_LABEL"
}

# ── Manual (no service manager) ───────────────────────────────────────────────
install_manual() {
    warn "No service manager detected — Blackhole is installed but will not start automatically."
    warn "Run manually: $INSTALL_DIR/$BINARY_NAME daemon start --hub $HUB_URL --config $CONFIG_DIR"
}

# ── Config directory ──────────────────────────────────────────────────────────
ensure_config_dir() {
    if [ "$(id -u)" = "0" ]; then
        mkdir -p "$CONFIG_DIR"
        chmod 700 "$CONFIG_DIR"
    else
        sudo mkdir -p "$CONFIG_DIR" 2>/dev/null || true
        sudo chmod 700 "$CONFIG_DIR" 2>/dev/null || true
    fi
}

# ── Credentials file ───────────────────────────────────────────────────────────
# Creates /etc/blackhole/credentials.json with device identity.
# NOTE: device_id and device_token are provisioned during enrollment — this creates
# a skeleton with the fields the agent/healerd need to resolve on first boot.
# On first run the agent should call the enrollment handler to get a device_id.
ensure_credentials() {
    local machine="${BH_MACHINE:-$(hostname)}"
    local nats_url="${BH_NATS_URL:-nats://127.0.0.1:4222}"
    # Only create if credentials.json does not yet exist (preserve enrollment data).
    if [ ! -f "$CONFIG_DIR/credentials.json" ]; then
        cat > "$CONFIG_DIR/credentials.json" << EOCRED
{
  "nats_url": "${nats_url}",
  "hub_nats_url": "${nats_url}",
  "machine_id": "${machine}",
  "device_id": "",
  "virtual_ip": "",
  "tenant_id": "",
  "api_url": "${HUB_URL}"
}
EOCRED
        chmod 600 "$CONFIG_DIR/credentials.json"
        log "credentials skeleton created: $CONFIG_DIR/credentials.json"
        log "NOTE: device_id and device_token will be provisioned during enrollment"
    fi
}

# ── Main ──────────────────────────────────────────────────────────────────────
main() {
    detect_platform
    detect_service_manager
    resolve_artifact
    install_binary
    verify_checksum "$INSTALL_DIR/blackhole"
    ensure_config_dir
    ensure_credentials
    verify_install

    case "$SERVICE_MGR" in
        systemd)  install_systemd ;;
        openrc)   install_openrc  ;;
        runit)    install_runit   ;;
        openwrt)  install_openwrt ;;
        launchd)  install_launchd ;;
        none)     install_manual  ;;
    esac

    printf '\n'
    log "Blackhole installed successfully"
    printf '\n'
    printf '  Next steps:\n'
    printf '    blackhole quickstart     # interactive onboarding wizard\n'
    printf '    blackhole login          # sign in or create account\n'
    printf '    blackhole daemon start   # start the mesh agent\n'
    printf '    blackhole status         # verify mesh connection\n'
    printf '    blackhole company create # create a virtual company\n'
    printf '\n'
    printf '  Docs: https://blackholemesh.dev/docs\n'
    printf '\n'
}

main "$@"
