#!/bin/sh
# ----------------------------------------------------------------------
#
#   Aloic CLI installer
#
#   Install:    curl -fsSL https://get.aloic.ai | sh
#   A version:  curl -fsSL https://get.aloic.ai | sh -s -- 0.1.18
#   Remove:     curl -fsSL https://get.aloic.ai | sh -s -- uninstall
#
#   Installs into your home folder. Needs Node.js 18 or newer, no root.
#   https://aloic.ai
#
# ----------------------------------------------------------------------

set -e

BASE="${ALOIC_INSTALL_BASE:-https://get.aloic.ai}"
HOME_DIR="$HOME/.aloic"
BIN_DIR="$HOME/.local/bin"

TARGET="$1"

case "$TARGET" in
    ""|latest|stable|uninstall|test) ;;
    *)
        if ! printf '%s' "$TARGET" \
            | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?$'; then
            echo "Usage: install.sh [latest|test|VERSION|uninstall]" >&2
            exit 1
        fi
        ;;
esac

if [ "$(id -u)" -eq 0 ] && [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ] && [ -z "${ALOIC_INSTALL_ALLOW_SUDO:-}" ]; then
    echo "Error: do not run this installer with sudo." >&2
    echo "" >&2
    echo "The Aloic CLI installs into your home directory and does not need root." >&2
    echo "Under sudo it would install into root's home instead of yours, and the" >&2
    echo "aloic command would not work from your own shell." >&2
    echo "" >&2
    echo "Run the same command again without sudo:" >&2
    echo "    curl -fsSL https://get.aloic.ai | sh" >&2
    exit 1
fi

if [ "$TARGET" = "uninstall" ]; then
    removed=0

    reply=""
    if { printf "Are you sure you want to uninstall the Aloic CLI? [y/N] " > /dev/tty; } 2>/dev/null; then
        IFS= read -r reply < /dev/tty 2>/dev/null || reply=""
        case "$reply" in
            y|Y|yes|YES) ;;
            *) echo "" > /dev/tty 2>/dev/null || true; echo "Nothing was removed."; exit 0 ;;
        esac
        echo "" > /dev/tty 2>/dev/null || true
    fi

    for path in "$HOME_DIR/versions" "$HOME_DIR/current" "$HOME_DIR/env" "$BIN_DIR/aloic"; do
        if [ -e "$path" ] || [ -L "$path" ]; then
            rm -rf "$path"
            echo "removed $path"
            removed=1
        fi
    done

    had_key=0
    if [ -f "$HOME_DIR/config.json" ]; then
        rm -f "$HOME_DIR/config.json"
        echo "removed $HOME_DIR/config.json"
        had_key=1
        removed=1
    fi

    rmdir "$HOME_DIR" 2>/dev/null || true

    for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" \
              "$HOME/.config/fish/config.fish"; do
        [ -f "$rc" ] || continue
        grep -q "aloic installer" "$rc" 2>/dev/null || continue
        tmp="$rc.aloic-tmp-$$"
        awk '
          /^# aloic installer$/ { skip = 2; if (blank) blank = 0; next }
          skip > 0 { skip--; if ($0 ~ /^(export PATH=|fish_add_path)/) next }
          { print }
        ' "$rc" > "$tmp" && mv "$tmp" "$rc"
        echo "cleaned $rc"
        removed=1
    done

    echo ""
    if [ "$removed" -eq 0 ]; then
        echo "Aloic was not installed here. Nothing to remove."
    else
        echo "Aloic removed. Open a new terminal for the PATH change to take effect."
        if [ "$had_key" -eq 1 ]; then
            echo "Removed the token from this machine. Revoke it in Settings on aloic.ai, under API tokens."
        fi
    fi
    exit 0
fi

case "$(uname -s)" in
    Darwin) os="darwin" ;;
    Linux)  os="linux" ;;
    MINGW*|MSYS*|CYGWIN*)
        echo "This installer is for macOS and Linux." >&2
        echo "On Windows, use WSL, or run the CLI directly with: npx aloic" >&2
        exit 1 ;;
    *) echo "Unsupported operating system: $(uname -s)" >&2; exit 1 ;;
esac

DOWNLOADER=""
if command -v curl >/dev/null 2>&1; then
    DOWNLOADER="curl"
elif command -v wget >/dev/null 2>&1; then
    DOWNLOADER="wget"
else
    echo "Either curl or wget is required but neither is installed." >&2
    exit 1
fi

if ! command -v node >/dev/null 2>&1; then
    echo "Node.js 18 or newer is required and was not found." >&2
    echo "" >&2
    echo "Install it, then run this again:" >&2
    if [ "$os" = "darwin" ]; then
        echo "    brew install node          https://nodejs.org/en/download" >&2
    else
        echo "    https://nodejs.org/en/download" >&2
    fi
    exit 1
fi

node_major=$(node -p "process.versions.node.split('.')[0]" 2>/dev/null || echo 0)
if [ "$node_major" -lt 18 ]; then
    echo "Node.js 18 or newer is required. Found $(node -v)." >&2
    echo "Upgrade Node, then run this again: https://nodejs.org/en/download" >&2
    exit 1
fi

download() {
    if [ "$DOWNLOADER" = "curl" ]; then
        if [ -n "$2" ]; then curl -fsSL -o "$2" "$1"; else curl -fsSL "$1"; fi
    else
        if [ -n "$2" ]; then wget -q -O "$2" "$1"; else wget -q -O - "$1"; fi
    fi
}

sha_of() {
    if [ "$os" = "darwin" ]; then shasum -a 256 "$1" | cut -d' ' -f1
    else sha256sum "$1" | cut -d' ' -f1; fi
}

CHAN="$BASE"
if [ "$TARGET" = "test" ]; then CHAN="$BASE/test"; TARGET=""; fi

if [ -z "$TARGET" ] || [ "$TARGET" = "latest" ] || [ "$TARGET" = "stable" ]; then
    version=$(download "$CHAN/latest" || true)
    version=$(echo "$version" | tr -d '[:space:]')
else
    version="$TARGET"
fi

if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+'; then
    echo "Could not determine which version to install." >&2
    echo "get.aloic.ai did not return a version number." >&2
    exit 1
fi

manifest=$(download "$CHAN/manifest.json" || true)
if [ -z "$manifest" ]; then
    echo "Could not reach get.aloic.ai to read the release manifest." >&2
    exit 1
fi

manifest_version=$(echo "$manifest" | tr -d '\n\r\t ' | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
if [ "$manifest_version" != "$version" ]; then
    echo "Version $version is not the published release ($manifest_version)." >&2
    echo "Only the current release is hosted here." >&2
    exit 1
fi

pairs=$(echo "$manifest" \
  | tr -d '\n\r\t' \
  | grep -o '"path"[[:space:]]*:[[:space:]]*"[^"]*"[^}]*"sha256"[[:space:]]*:[[:space:]]*"[a-f0-9]\{64\}"' \
  | sed 's/.*"path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*"sha256"[[:space:]]*:[[:space:]]*"\([a-f0-9]\{64\}\)".*/\1 \2/')

if [ -z "$pairs" ]; then
    echo "The release manifest could not be read." >&2
    exit 1
fi

echo "Installing aloic $version"

stage="$HOME_DIR/.staging-$$"
rm -rf "$stage"
mkdir -p "$stage"
cleanup() { rm -rf "$stage"; }
trap cleanup EXIT

count=0
sizes=$(echo "$manifest" | tr -d '\n\r\t' | grep -o '{[^{}]*"path"[^{}]*}' \
  | sed -n 's/.*"path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*"size"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1 \2/p')
size_of() { printf '%s\n' "$sizes" | awk -v p="$1" '$1 == p { print $2; exit }'; }
total=$(printf '%s\n' "$sizes" | awk '{ n += $2 } END { print n + 0 }')
files=$(printf '%s\n' "$pairs" | grep -c .)
if [ "$total" -eq 0 ]; then total=$files; fi
done_bytes=0

bar() {
    [ -t 2 ] || return 0
    width=30
    filled=$((width * $1 / ($2 > 0 ? $2 : 1)))
    pct=$((100 * $1 / ($2 > 0 ? $2 : 1)))
    line="" i=0
    while [ "$i" -lt "$width" ]; do
        if [ "$i" -lt "$filled" ]; then line="$line="; else line="$line "; fi
        i=$((i + 1))
    done
    if [ -n "$sizes" ]; then
        amount=$(awk -v a="$1" -v b="$2" 'function h(n) { return n >= 1e6 ? sprintf("%.1f MB", n / 1e6) : sprintf("%d KB", (n + 999) / 1000) }
            BEGIN { printf "%s / %s", h(a), h(b) }')
    else
        amount="$1/$2 files"
    fi
    printf '\r\033[2K  [%s] %3d%%  \033[90m%s\033[0m' "$line" "$pct" "$amount" >&2
}
bar 0 "$total"

while read -r path sum; do
    [ -z "$path" ] && continue
    dest="$stage/$path"
    mkdir -p "$(dirname "$dest")"
    if ! download "$CHAN/$version/$path" "$dest"; then
        if [ -t 2 ]; then echo >&2; fi
        echo "Download failed: $path" >&2
        exit 1
    fi
    got=$(sha_of "$dest")
    if [ "$got" != "$sum" ]; then
        if [ -t 2 ]; then echo >&2; fi
        echo "Checksum verification failed for $path" >&2
        echo "  expected $sum" >&2
        echo "  got      $got" >&2
        exit 1
    fi
    count=$((count + 1))
    if [ -n "$sizes" ]; then
        done_bytes=$((done_bytes + $(size_of "$path")))
        bar "$done_bytes" "$total"
    else
        bar "$count" "$total"
    fi
done <<EOF
$pairs
EOF
if [ -t 2 ]; then printf '\r\033[2K' >&2; fi

chmod +x "$stage/aloic.mjs" 2>/dev/null || true

target="$HOME_DIR/versions/$version"
mkdir -p "$HOME_DIR/versions"
rm -rf "$target"
mv "$stage" "$target"
trap - EXIT

ln -sfn "$target" "$HOME_DIR/current"

mkdir -p "$BIN_DIR"
cat > "$BIN_DIR/aloic" <<LAUNCHER
#!/bin/sh
exec node "$HOME_DIR/current/aloic.mjs" "\$@"
LAUNCHER
chmod +x "$BIN_DIR/aloic"

on_path=0
case ":$PATH:" in
    *":$BIN_DIR:"*) on_path=1 ;;
esac

added_to=""
if [ "$on_path" -eq 0 ]; then
    rc=""
    case "$(basename "${SHELL:-}")" in
        zsh)  rc="$HOME/.zshrc" ;;
        bash) if [ -f "$HOME/.bashrc" ]; then rc="$HOME/.bashrc"; else rc="$HOME/.bash_profile"; fi ;;
        fish) rc="$HOME/.config/fish/config.fish" ;;
        *)    rc="$HOME/.profile" ;;
    esac
    mkdir -p "$(dirname "$rc")"
    if ! grep -q "aloic installer" "$rc" 2>/dev/null; then
        {
            echo ""
            echo "# aloic installer"
            if [ "$(basename "${SHELL:-}")" = "fish" ]; then
                echo "fish_add_path $BIN_DIR"
            else
                echo "export PATH=\"$BIN_DIR:\$PATH\""
            fi
        } >> "$rc"
    fi
    added_to="$rc"

    cat > "$HOME_DIR/env" <<'ENVFILE'
#!/bin/sh
case ":${PATH}:" in
    *":$HOME/.local/bin:"*) ;;
    *) export PATH="$HOME/.local/bin:$PATH" ;;
esac
ENVFILE
fi

if ! "$BIN_DIR/aloic" --version >/dev/null 2>&1; then
    echo "Installed, but 'aloic --version' did not run. Try: node $HOME_DIR/current/aloic.mjs --version" >&2
    exit 1
fi

bold="" grey="" reset=""
if [ -t 1 ]; then
    esc=$(printf '\033')
    bold="$esc[1m"; grey="$esc[90m"; reset="$esc[0m"
fi

tilde() {
    case "$1" in
        "$HOME"/*) printf '~%s' "${1#"$HOME"}" ;;
        *) printf '%s' "$1" ;;
    esac
}

echo ""
echo "${bold}Welcome to Aloic.${reset} ${grey}$version, $count files, verified${reset}"
echo ""

handed=0
WEB="${ALOIC_WEB:-https://auth.aloic.ai}"
web_up=0
if [ "$DOWNLOADER" = "curl" ]; then
    curl -fsS -o /dev/null -m 4 "$WEB" >/dev/null 2>&1 && web_up=1
else
    wget -q -O /dev/null -T 4 "$WEB" >/dev/null 2>&1 && web_up=1
fi

if [ "$web_up" -eq 1 ] && { printf "" > /dev/tty; } 2>/dev/null; then
    echo ""
    status=0
    "$BIN_DIR/aloic" < /dev/tty > /dev/tty 2>&1 || status=$?
    if [ "$status" -eq 0 ] || [ "$status" -eq 6 ]; then handed=1; fi
fi

if [ "$handed" -eq 1 ]; then
    if [ "$on_path" -eq 0 ]; then
        echo ""
        echo "${bold}aloic${reset} works by name in a new terminal. In this one:"
        echo ""
        echo "    source $(tilde "$HOME_DIR/env")"
        echo ""
    fi
    exit 0
fi

if [ "$on_path" -eq 0 ]; then
    echo "The ${bold}aloic${reset} command is at $(tilde "$BIN_DIR/aloic")."
    echo "$(tilde "$BIN_DIR") is on your PATH in $(tilde "$added_to"), so a ${bold}new terminal${reset}"
    echo "will find it by name. To use it in this one:"
    echo ""
    echo "    source $(tilde "$HOME_DIR/env")"
    echo ""
fi

run="aloic"
if [ "$on_path" -eq 0 ]; then run="$(tilde "$BIN_DIR/aloic")"; fi

echo "Run next: ${bold}$run${reset}"
echo "${grey}Agents: run $run login --agent${reset}"
echo ""
