#!/bin/sh
# 3pm installer — also the updater: run it again to move to the latest version.
#   curl -fsSL https://3pm.edgecr4ft.net/install.sh | sh
#   curl -fsSL https://3pm.edgecr4ft.net/install.sh | sh -s v0.2.0
# Environment overrides:
#   THREEPM_INSTALL         install dir (default: $HOME/.3pm)
#   THREEPM_CDN_URL         download base (default: https://3pm.edgecr4ft.net)
#   THREEPM_VERSION         version to install (default: latest)
#   THREEPM_NO_MODIFY_PATH  set to skip shell-config PATH edits
set -eu

CDN_URL="${THREEPM_CDN_URL:-https://3pm.edgecr4ft.net}"
GITHUB_REPO="Unitech/3pm"
INSTALL_DIR="${THREEPM_INSTALL:-$HOME/.3pm}"
BIN_DIR="$INSTALL_DIR/bin"
# Bumped by /release; used only if the GitHub API is unreachable.
FALLBACK_VERSION="0.1.0"

say() { printf '[3PM] %s\n' "$1"; }
err() { printf '[3PM][ERROR] %s\n' "$1" >&2; exit 1; }

download() { # $1=url $2=dest ; returns non-zero on failure
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL "$1" -o "$2"
    elif command -v wget >/dev/null 2>&1; then
        wget -q "$1" -O "$2"
    else
        err "curl or wget is required"
    fi
}

# --- platform ---------------------------------------------------------------
case "$(uname -s) $(uname -m)" in
    "Darwin x86_64")                TARGET=macos-x86_64 ;;
    "Darwin arm64")                 TARGET=macos-arm64 ;;
    "Linux x86_64")                 TARGET=linux-x86_64 ;;
    "Linux aarch64" | "Linux arm64") TARGET=linux-arm64 ;;
    *) err "unsupported platform: $(uname -s) $(uname -m)" ;;
esac
if [ -r /proc/version ] && grep -qi alpine /proc/version 2>/dev/null; then
    say "warning: musl/Alpine detected — the glibc build may not run"
fi

# --- version ----------------------------------------------------------------
VERSION="${1:-${THREEPM_VERSION:-}}"
if [ -z "$VERSION" ]; then
    # Written last by `just publish`, so it only ever names a complete version.
    VERSION=$( (download "$CDN_URL/latest/VERSION" - 2>/dev/null || true) | tr -d '[:space:]' )
fi
if [ -z "$VERSION" ]; then
    VERSION=$( (download "https://api.github.com/repos/$GITHUB_REPO/releases/latest" - 2>/dev/null || true) \
        | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -1 )
fi
[ -n "$VERSION" ] || VERSION="$FALLBACK_VERSION"
VERSION="${VERSION#v}"

ARCHIVE="3pm-$TARGET.tar.gz"
PRIMARY_URL="$CDN_URL/v$VERSION/$ARCHIVE"
FALLBACK_URL="https://github.com/$GITHUB_REPO/releases/download/v$VERSION/$ARCHIVE"
PRIMARY_SUMS="$CDN_URL/v$VERSION/checksums.txt"
FALLBACK_SUMS="https://github.com/$GITHUB_REPO/releases/download/v$VERSION/checksums.txt"

# --- download + verify --------------------------------------------------------
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT

say "Downloading 3pm v$VERSION ($TARGET)"
if ! download "$PRIMARY_URL" "$TMP_DIR/$ARCHIVE" 2>/dev/null; then
    say "CDN unavailable, falling back to GitHub releases"
    download "$FALLBACK_URL" "$TMP_DIR/$ARCHIVE" || err "download failed: $FALLBACK_URL"
fi

if download "$PRIMARY_SUMS" "$TMP_DIR/checksums.txt" 2>/dev/null \
    || download "$FALLBACK_SUMS" "$TMP_DIR/checksums.txt" 2>/dev/null; then
    EXPECTED=$(grep " $ARCHIVE\$" "$TMP_DIR/checksums.txt" | cut -d' ' -f1)
    if command -v sha256sum >/dev/null 2>&1; then
        ACTUAL=$(sha256sum "$TMP_DIR/$ARCHIVE" | cut -d' ' -f1)
    else
        ACTUAL=$(shasum -a 256 "$TMP_DIR/$ARCHIVE" | cut -d' ' -f1)
    fi
    [ -n "$EXPECTED" ] && [ "$EXPECTED" = "$ACTUAL" ] || err "checksum mismatch for $ARCHIVE"
    say "Checksum verified"
else
    say "warning: checksums.txt unavailable, skipping verification"
fi

# --- install ------------------------------------------------------------------
tar -xzf "$TMP_DIR/$ARCHIVE" -C "$TMP_DIR"
mkdir -p "$BIN_DIR"
if [ -f "$TMP_DIR/3pm-$TARGET/3pm" ]; then
    mv "$TMP_DIR/3pm-$TARGET/3pm" "$BIN_DIR/3pm"
elif [ -f "$TMP_DIR/3pm" ]; then
    mv "$TMP_DIR/3pm" "$BIN_DIR/3pm"
else
    err "archive layout not recognized"
fi
chmod +x "$BIN_DIR/3pm"
say "Installed to $BIN_DIR/3pm"
# Bundled modules (`3pm module list`, `3pm serve`, …) sit next to the
# binary; 3pm looks them up in $BIN_DIR/modules.
if [ -d "$TMP_DIR/3pm-$TARGET/modules" ]; then
    rm -rf "$BIN_DIR/modules"
    mv "$TMP_DIR/3pm-$TARGET/modules" "$BIN_DIR/modules"
    say "Bundled modules: $(ls "$BIN_DIR/modules" | tr '\n' ' ')"
fi

# --- update a running daemon ----------------------------------------------------
# A daemon started from this install hands off to the new binary in place
# (`3pm update`: apps keep running, modules refreshed). No daemon: nothing
# to do — `3pm update` would start one.
THREEPM_DIR="${THREEPM_HOME:-$HOME/.3pm}"
DAEMON_PID=$(cat "$THREEPM_DIR/3pm.pid" 2>/dev/null || true)
if [ -n "$DAEMON_PID" ] && [ -S "$THREEPM_DIR/rpc.sock" ] && kill -0 "$DAEMON_PID" 2>/dev/null; then
    case "$(ps -o command= -p "$DAEMON_PID" 2>/dev/null)" in
        "$BIN_DIR/3pm"*)
            say "Running daemon found: handing it off to v$VERSION"
            "$BIN_DIR/3pm" update || err "3pm update failed (see above)"
            ;;
        *) say "A 3pm daemon runs from another binary; switch it with: $BIN_DIR/3pm update" ;;
    esac
fi

# --- PATH ---------------------------------------------------------------------
if [ -z "${THREEPM_NO_MODIFY_PATH:-}" ]; then
    PATH_LINE="export PATH=\"$BIN_DIR:\$PATH\""
    for rc in "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.zshrc"; do
        if [ -f "$rc" ] && ! grep -q "$BIN_DIR" "$rc" 2>/dev/null; then
            printf '\n# 3pm\n%s\n' "$PATH_LINE" >> "$rc"
            say "Added $BIN_DIR to PATH in $rc"
        fi
    done
    if command -v fish >/dev/null 2>&1; then
        FISH_CONF="$HOME/.config/fish/conf.d/3pm.fish"
        if [ ! -f "$FISH_CONF" ]; then
            mkdir -p "$(dirname "$FISH_CONF")"
            printf 'fish_add_path %s\n' "$BIN_DIR" > "$FISH_CONF"
        fi
    fi
    case ":$PATH:" in
        *":$BIN_DIR:"*) ;;
        *) say "Restart your shell (or export PATH=\"$BIN_DIR:\$PATH\") to use 3pm" ;;
    esac
fi

say "Done — try: 3pm start app.js"
