#!/bin/sh
# RunWisp installer — https://get.runwisp.com
#
# Downloads the latest RunWisp release tarball from GitHub, verifies its
# SHA-256 checksum, and installs the binary into a directory on your PATH.
#
# Usage:
#   curl -fsSL https://get.runwisp.com | sh
#
# Environment variables:
#   RUNWISP_VERSION      Tag to install (e.g. v0.1.2). Default: latest
#   RUNWISP_INSTALL_DIR  Target directory for the binary.
#                        Default: /usr/local/bin when writable, else ~/.local/bin

set -eu

REPO="runwisp/runwisp"
VERSION="${RUNWISP_VERSION:-latest}"
INSTALL_DIR="${RUNWISP_INSTALL_DIR:-}"

if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
    BOLD=$(printf '\033[1m')
    DIM=$(printf '\033[2m')
    RED=$(printf '\033[31m')
    GREEN=$(printf '\033[32m')
    YELLOW=$(printf '\033[33m')
    RESET=$(printf '\033[0m')
else
    BOLD=""
    DIM=""
    RED=""
    GREEN=""
    YELLOW=""
    RESET=""
fi

err() {
    printf '%serror:%s %s\n' "$RED" "$RESET" "$*" >&2
    exit 1
}

info() {
    printf '%s==>%s %s\n' "$GREEN" "$RESET" "$*"
}

warn() {
    printf '%swarning:%s %s\n' "$YELLOW" "$RESET" "$*" >&2
}

require() {
    command -v "$1" >/dev/null 2>&1 || err "$1 is required but not installed"
}

detect_target() {
    os=$(uname -s)
    arch=$(uname -m)

    case "$os" in
        Linux) os="linux" ;;
        Darwin) os="darwin" ;;
        *) err "unsupported OS: $os (RunWisp supports Linux, macOS, and WSL)" ;;
    esac

    case "$arch" in
        x86_64|amd64) arch="x64" ;;
        aarch64|arm64) arch="arm64" ;;
        *) err "unsupported architecture: $arch" ;;
    esac

    printf '%s-%s' "$os" "$arch"
}

choose_install_dir() {
    if [ -n "$INSTALL_DIR" ]; then
        printf '%s' "$INSTALL_DIR"
        return
    fi

    if [ -w /usr/local/bin ] 2>/dev/null; then
        printf '/usr/local/bin'
    elif [ "$(id -u)" -eq 0 ]; then
        printf '/usr/local/bin'
    else
        printf '%s/.local/bin' "$HOME"
    fi
}

sha256() {
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$1" | awk '{print $1}'
    elif command -v shasum >/dev/null 2>&1; then
        shasum -a 256 "$1" | awk '{print $1}'
    else
        err "neither sha256sum nor shasum is installed; cannot verify download"
    fi
}

# Pick a downloader once. Prefer curl; fall back to wget (BusyBox wget on
# Alpine works for both HTTPS and redirects when ca-certificates is present).
DOWNLOADER=""
select_downloader() {
    if command -v curl >/dev/null 2>&1; then
        DOWNLOADER="curl"
    elif command -v wget >/dev/null 2>&1; then
        DOWNLOADER="wget"
    else
        err "curl or wget is required but neither is installed"
    fi
}

# Download URL to a file. With "progress" as the third arg, show a progress
# bar; otherwise stay silent.
download_to() {
    url=$1
    out=$2
    mode=${3:-}
    case "$DOWNLOADER" in
        curl)
            if [ "$mode" = "progress" ]; then
                curl -fL --progress-bar -o "$out" "$url"
            else
                curl -fsSL -o "$out" "$url"
            fi
            ;;
        wget)
            if [ "$mode" = "progress" ]; then
                wget -O "$out" "$url"
            else
                wget -q -O "$out" "$url"
            fi
            ;;
    esac
}

# Fetch URL to stdout silently.
download_stdout() {
    case "$DOWNLOADER" in
        curl) curl -fsSL "$1" ;;
        wget) wget -q -O - "$1" ;;
    esac
}

# Resolve the most recent release tag, including prereleases.
# GitHub's /releases/latest redirect excludes prereleases, so query the API
# listing endpoint instead.
resolve_latest_version() {
    download_stdout "https://api.github.com/repos/${REPO}/releases?per_page=1" 2>/dev/null \
        | grep -m1 '"tag_name"' \
        | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/'
}

main() {
    select_downloader
    require tar
    require uname

    target=$(detect_target)
    archive="runwisp-${target}.tar.gz"

    if [ "$VERSION" = "latest" ]; then
        VERSION=$(resolve_latest_version)
        [ -n "$VERSION" ] || err "failed to resolve latest release tag from GitHub API"
    fi

    case "$VERSION" in
        v*) ;;
        *) VERSION="v${VERSION}" ;;
    esac
    base="https://github.com/${REPO}/releases/download/${VERSION}"
    version_label="$VERSION"

    info "Installing RunWisp ${BOLD}${version_label}${RESET} for ${BOLD}${target}${RESET}"

    tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t runwisp)
    trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP

    info "Downloading ${archive}"
    if ! download_to "${base}/${archive}" "${tmpdir}/${archive}" progress; then
        err "failed to download ${base}/${archive}"
    fi

    info "Verifying checksum"
    if ! download_to "${base}/checksums-sha256.txt" "${tmpdir}/checksums.txt"; then
        err "failed to download checksum file"
    fi

    expected=$(grep "  ${archive}\$" "${tmpdir}/checksums.txt" | awk '{print $1}')
    if [ -z "$expected" ]; then
        err "no checksum entry found for ${archive}"
    fi

    actual=$(sha256 "${tmpdir}/${archive}")
    if [ "$expected" != "$actual" ]; then
        err "checksum mismatch for ${archive} (expected ${expected}, got ${actual})"
    fi

    info "Extracting"
    if ! tar -xzf "${tmpdir}/${archive}" -C "$tmpdir" runwisp; then
        err "failed to extract archive"
    fi

    install_dir=$(choose_install_dir)
    if ! mkdir -p "$install_dir" 2>/dev/null; then
        err "failed to create ${install_dir}"
    fi

    target_path="${install_dir}/runwisp"
    if [ ! -w "$install_dir" ]; then
        if command -v sudo >/dev/null 2>&1; then
            warn "${install_dir} is not writable; re-running install step with sudo"
            sudo install -m 0755 "${tmpdir}/runwisp" "$target_path" \
                || err "failed to install runwisp to ${target_path}"
        else
            err "${install_dir} is not writable and sudo is not available"
        fi
    else
        install -m 0755 "${tmpdir}/runwisp" "$target_path" \
            || err "failed to install runwisp to ${target_path}"
    fi

    printf '\n%sRunWisp installed to %s%s%s\n' \
        "$GREEN" "$BOLD" "$target_path" "$RESET"

    case ":$PATH:" in
        *":${install_dir}:"*)
            ;;
        *)
            printf '\n%s%s is not in your PATH.%s\n' "$YELLOW" "$install_dir" "$RESET"
            printf 'Add this line to your shell profile (e.g. ~/.bashrc, ~/.zshrc):\n\n'
            printf '  %sexport PATH="%s:$PATH"%s\n' "$BOLD" "$install_dir" "$RESET"
            ;;
    esac

    printf '\nNext steps:\n'
    printf '  %s1.%s Create a %srunwisp.toml%s — see https://github.com/%s/blob/main/apps/runwisp/runwisp.example.toml\n' \
        "$DIM" "$RESET" "$BOLD" "$RESET" "$REPO"
    printf '  %s2.%s Run %srunwisp%s and open http://localhost:8080\n\n' \
        "$DIM" "$RESET" "$BOLD" "$RESET"
}

main "$@"
