#!/bin/sh
# This script installs OpenELF on Linux and macOS.
# It detects the current operating system architecture and installs the
# appropriate version of OpenELF.
#
# Usage:
#   curl -fsSL https://update.openelf.ai/install.sh | sh
#
# Environment variables:
#   OPENELF_VERSION   - Install a specific version instead of latest
#   OPENELF_NO_START  - Set to skip auto-starting on macOS
#   OPENELF_UPDATE_ORIGIN - Override the mutable update endpoint (default: https://update.openelf.ai)
#   OPENELF_RELEASE_REPO  - Override the immutable release repo (default: openelf-labs/releases)

# =========================================================
# Utility functions (top-level for testability)
# =========================================================

_openelf_red=""
_openelf_green=""
_openelf_plain=""

_init_colors() {
    _openelf_red="$( (/usr/bin/tput bold || :; /usr/bin/tput setaf 1 || :) 2>&-)"
    _openelf_green="$( (/usr/bin/tput bold || :; /usr/bin/tput setaf 2 || :) 2>&-)"
    _openelf_plain="$( (/usr/bin/tput sgr0 || :) 2>&-)"
}

status() { echo ">>> $*" >&2; }
error() { echo "${_openelf_red}ERROR:${_openelf_plain} $*" >&2; exit 1; }
warning() { echo "${_openelf_red}WARNING:${_openelf_plain} $*" >&2; }

available() { command -v "$1" >/dev/null; }

require() {
    MISSING=''
    for TOOL in $*; do
        if ! available "$TOOL"; then
            MISSING="$MISSING $TOOL"
        fi
    done
    echo "$MISSING"
}

OPENELF_UPDATE_ORIGIN="${OPENELF_UPDATE_ORIGIN:-https://update.openelf.ai}"
OPENELF_RELEASE_REPO="${OPENELF_RELEASE_REPO:-openelf-labs/releases}"

normalize_version() {
    printf "%s" "${1#v}"
}

release_tag() {
    printf "v%s" "$(normalize_version "$1")"
}

latest_manifest_url() {
    printf "%s/latest.json" "$OPENELF_UPDATE_ORIGIN"
}

version_manifest_url() {
    printf "https://github.com/%s/releases/download/%s/manifest.json" \
        "$OPENELF_RELEASE_REPO" "$(release_tag "$1")"
}

version_release_asset_url() {
    printf "https://github.com/%s/releases/download/%s/%s" \
        "$OPENELF_RELEASE_REPO" "$(release_tag "$1")" "$2"
}

# Parse JSON fields using grep/sed (no jq dependency).
# These functions operate on the MANIFEST variable.
json_value() {
    # Extract a top-level string value: json_value "version"
    echo "$MANIFEST" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -1 | sed 's/.*:.*"\([^"]*\)"/\1/'
}

json_asset_field() {
    # Extract a field from a specific asset block: json_asset_field "linux-amd64" "url"
    _JAF_PLATFORM="$1"
    _JAF_FIELD="$2"
    echo "$MANIFEST" | \
        awk -v p="\"$_JAF_PLATFORM\"" -v f="\"$_JAF_FIELD\"" '
            $0 ~ p { found=1 }
            found && /\}/ { found=0 }
            found && $0 ~ f { print; exit }
        ' | sed 's/.*:[[:space:]]*"//' | sed 's/"[[:space:]]*,\{0,1\}[[:space:]]*$//'
}

json_asset_size() {
    _JAS_PLATFORM="$1"
    echo "$MANIFEST" | \
        awk -v p="\"$_JAS_PLATFORM\"" '
            $0 ~ p { found=1 }
            found && /\}/ { found=0 }
            found && /\"size\"/ { print; exit }
        ' | sed 's/.*:[[:space:]]*//' | sed 's/[^0-9].*//'
}

# SHA256 verification
verify_sha256() {
    _VS_FILE="$1"
    _VS_EXPECTED="$2"

    if [ -z "$_VS_EXPECTED" ]; then
        warning "No SHA256 checksum available, skipping verification."
        return 0
    fi

    _VS_ACTUAL=""
    if available shasum; then
        _VS_ACTUAL=$(shasum -a 256 "$_VS_FILE" | awk '{print $1}')
    elif available sha256sum; then
        _VS_ACTUAL=$(sha256sum "$_VS_FILE" | awk '{print $1}')
    else
        warning "Neither shasum nor sha256sum found, skipping verification."
        return 0
    fi

    if [ "$_VS_ACTUAL" != "$_VS_EXPECTED" ]; then
        error "SHA256 mismatch: expected $_VS_EXPECTED, got $_VS_ACTUAL"
    fi

    status "SHA256 verified."
}

# Generate systemd service file content
generate_systemd_service() {
    _GSS_BINDIR="$1"
    _GSS_PATH="$2"
    cat <<SVCEOF
[Unit]
Description=OpenELF AI Agent
After=network-online.target

[Service]
ExecStart=$_GSS_BINDIR/openelf --data-dir /usr/share/openelf
User=openelf
Group=openelf
Restart=always
RestartSec=3
Environment="PATH=$_GSS_PATH"

[Install]
WantedBy=default.target
SVCEOF
}

# =========================================================
# Main install logic (wrapped to prevent partial execution)
# =========================================================
main() {

set -eu

_init_colors

TEMP_DIR=$(mktemp -d)
_openelf_cleanup() { rm -rf "$TEMP_DIR"; }
trap _openelf_cleanup EXIT

# --- OS and architecture detection ---

OS="$(uname -s)"
ARCH=$(uname -m)
case "$ARCH" in
    x86_64)        ARCH="amd64" ;;
    aarch64|arm64) ARCH="arm64" ;;
    *)             error "Unsupported architecture: $ARCH" ;;
esac

# --- Fetch latest version from the mutable update alias ---

MANIFEST_URL="$(latest_manifest_url)"

NEEDS=$(require curl)
if [ -n "$NEEDS" ]; then
    error "curl is required but not found. Please install curl and try again."
fi

status "Fetching latest version..."
MANIFEST=$(curl -fsSL "$MANIFEST_URL")
if [ -z "$MANIFEST" ]; then
    error "Failed to fetch version manifest from $MANIFEST_URL"
fi

VERSION=$(normalize_version "$(json_value "version")")
if [ -z "$VERSION" ]; then
    error "Could not determine latest version from manifest."
fi

# Honor OPENELF_VERSION override
if [ -n "${OPENELF_VERSION:-}" ]; then
    VERSION="$(normalize_version "$OPENELF_VERSION")"
    status "Using requested version: ${VERSION}"
    # Re-fetch the immutable manifest for the pinned version
    MANIFEST=$(curl -fsSL "$(version_manifest_url "$VERSION")") || \
        error "Failed to fetch manifest for version ${VERSION}. Is this a valid release?"
else
    status "Latest version: ${VERSION}"
fi

###########################################
# macOS
###########################################

if [ "$OS" = "Darwin" ]; then
    NEEDS=$(require curl unzip)
    if [ -n "$NEEDS" ]; then
        error "The following tools are required but missing:$NEEDS"
    fi

    PLATFORM_KEY="darwin-${ARCH}"
    APP_ZIP_URL="$(version_release_asset_url "$VERSION" "OpenELF-darwin.zip")"
    BINARY_URL=$(json_asset_field "$PLATFORM_KEY" "url")
    BINARY_SHA256=$(json_asset_field "$PLATFORM_KEY" "sha256")

    # Check if .app zip is available
    APP_MODE=false
    if curl -fsSL --head "$APP_ZIP_URL" >/dev/null 2>&1; then
        APP_MODE=true
    fi

    if [ "$APP_MODE" = true ]; then
        # --- .app bundle install (like Ollama) ---

        if pgrep -x OpenELF >/dev/null 2>&1; then
            status "Stopping running OpenELF instance..."
            pkill -x OpenELF 2>/dev/null || true
            sleep 2
        fi

        if [ -d "/Applications/OpenELF.app" ]; then
            status "Removing existing OpenELF installation..."
            rm -rf "/Applications/OpenELF.app" 2>/dev/null || \
                sudo rm -rf "/Applications/OpenELF.app"
        fi

        status "Downloading OpenELF.app..."
        curl --fail --show-error --location --progress-bar \
            -o "$TEMP_DIR/OpenELF-darwin.zip" "$APP_ZIP_URL"

        # Verify .app zip SHA256 from checksums.txt
        APP_ZIP_SHA256=""
        CHECKSUMS_URL="$(version_release_asset_url "$VERSION" "checksums.txt")"
        CHECKSUMS=$(curl -fsSL "$CHECKSUMS_URL" 2>/dev/null) || true
        if [ -n "$CHECKSUMS" ]; then
            APP_ZIP_SHA256=$(echo "$CHECKSUMS" | grep "OpenELF-darwin.zip" | awk '{print $1}')
        fi
        verify_sha256 "$TEMP_DIR/OpenELF-darwin.zip" "$APP_ZIP_SHA256"

        status "Installing to /Applications (may require password)..."
        unzip -q "$TEMP_DIR/OpenELF-darwin.zip" -d "$TEMP_DIR"
        mv "$TEMP_DIR/OpenELF.app" "/Applications/" 2>/dev/null || \
            sudo mv "$TEMP_DIR/OpenELF.app" "/Applications/"

        # Create CLI symlink
        if [ ! -L "/usr/local/bin/openelf" ] || [ "$(readlink "/usr/local/bin/openelf")" != "/Applications/OpenELF.app/Contents/Resources/openelf" ]; then
            status "Adding 'openelf' command to PATH (may require password)..."
            mkdir -p "/usr/local/bin" 2>/dev/null || sudo mkdir -p "/usr/local/bin"
            ln -sf "/Applications/OpenELF.app/Contents/Resources/openelf" "/usr/local/bin/openelf" 2>/dev/null || \
                sudo ln -sf "/Applications/OpenELF.app/Contents/Resources/openelf" "/usr/local/bin/openelf"
        fi

        if [ -z "${OPENELF_NO_START:-}" ]; then
            status "Starting OpenELF..."
            open -a OpenELF --args hidden
        fi
    else
        # --- Bare binary fallback ---
        warning ".app bundle not available, installing CLI binary only."

        if [ -z "$BINARY_URL" ]; then
            error "No download available for platform: $PLATFORM_KEY"
        fi

        status "Downloading openelf ${VERSION}..."
        curl --fail --show-error --location --progress-bar \
            -o "$TEMP_DIR/openelf.tar.gz" "$BINARY_URL"

        verify_sha256 "$TEMP_DIR/openelf.tar.gz" "$BINARY_SHA256"

        tar -xzf "$TEMP_DIR/openelf.tar.gz" -C "$TEMP_DIR"

        status "Installing to /usr/local/bin (may require password)..."
        mkdir -p "/usr/local/bin" 2>/dev/null || sudo mkdir -p "/usr/local/bin"
        install -m 755 "$TEMP_DIR/openelf" "/usr/local/bin/openelf" 2>/dev/null || \
            sudo install -m 755 "$TEMP_DIR/openelf" "/usr/local/bin/openelf"
    fi

    status "Install complete. Run ${_openelf_green}'openelf'${_openelf_plain} from the command line."
    status "Dashboard: http://127.0.0.1:11435"
    exit 0
fi

###########################################
# Linux
###########################################

[ "$OS" = "Linux" ] || error "This script is intended to run on Linux and macOS only."

IS_WSL2=false

KERN=$(uname -r)
case "$KERN" in
    *icrosoft*WSL2 | *icrosoft*wsl2) IS_WSL2=true ;;
    *icrosoft) error "Microsoft WSL1 is not currently supported. Please use WSL2 with 'wsl --set-version <distro> 2'" ;;
    *) ;;
esac

SUDO=
if [ "$(id -u)" -ne 0 ]; then
    if ! available sudo; then
        error "This script requires superuser permissions. Please re-run as root."
    fi
    SUDO="sudo"
fi

NEEDS=$(require curl awk grep sed tee)
if [ -n "$NEEDS" ]; then
    error "The following tools are required but missing:$NEEDS"
fi

# Determine install directory
for BINDIR in /usr/local/bin /usr/bin /bin; do
    echo "$PATH" | grep -q "$BINDIR" && break || continue
done

# Stop existing instance if running
if pgrep -x openelf >/dev/null 2>&1; then
    status "Stopping running OpenELF instance..."
    $SUDO pkill -x openelf 2>/dev/null || true
    sleep 2
fi

# Resolve download
PLATFORM_KEY="linux-${ARCH}"
DOWNLOAD_URL=$(json_asset_field "$PLATFORM_KEY" "url")
DOWNLOAD_SHA256=$(json_asset_field "$PLATFORM_KEY" "sha256")

if [ -z "$DOWNLOAD_URL" ]; then
    error "No download available for platform: $PLATFORM_KEY"
fi

# Download and install binary
status "Downloading openelf ${VERSION}..."
curl --fail --show-error --location --progress-bar \
    -o "$TEMP_DIR/openelf.tar.gz" "$DOWNLOAD_URL"

verify_sha256 "$TEMP_DIR/openelf.tar.gz" "$DOWNLOAD_SHA256"

status "Installing to ${BINDIR}..."
tar -xzf "$TEMP_DIR/openelf.tar.gz" -C "$TEMP_DIR"
$SUDO install -o0 -g0 -m755 -d "$BINDIR"
$SUDO install -o0 -g0 -m755 "$TEMP_DIR/openelf" "$BINDIR/openelf"

# --- systemd configuration ---

configure_systemd() {
    if ! id openelf >/dev/null 2>&1; then
        status "Creating openelf user..."
        $SUDO useradd -r -s /bin/false -U -m -d /usr/share/openelf openelf
    fi

    # Ensure data directory exists and is owned by openelf
    $SUDO mkdir -p /usr/share/openelf
    $SUDO chown openelf:openelf /usr/share/openelf

    status "Creating openelf systemd service..."
    generate_systemd_service "$BINDIR" "$PATH" | $SUDO tee /etc/systemd/system/openelf.service >/dev/null

    SYSTEMCTL_RUNNING="$(systemctl is-system-running || true)"
    case $SYSTEMCTL_RUNNING in
        running|degraded)
            status "Enabling and starting openelf service..."
            $SUDO systemctl daemon-reload
            $SUDO systemctl enable openelf

            _openelf_start_service() { $SUDO systemctl restart openelf; }
            trap '_openelf_start_service; _openelf_install_success; _openelf_cleanup' EXIT
            ;;
        *)
            warning "systemd is not running."
            if [ "$IS_WSL2" = true ]; then
                warning "See https://learn.microsoft.com/en-us/windows/wsl/systemd#how-to-enable-systemd to enable it."
            fi
            ;;
    esac
}

_openelf_install_success() {
    status "The OpenELF API is now available at ${_openelf_green}127.0.0.1:11435${_openelf_plain}"
    status "Dashboard: ${_openelf_green}http://127.0.0.1:11435${_openelf_plain}"
    status "Install complete. Run ${_openelf_green}'openelf'${_openelf_plain} from the command line."
}
trap '_openelf_install_success; _openelf_cleanup' EXIT

# Everything from this point onwards is optional.

if available systemctl; then
    configure_systemd
fi

}

# Allow sourcing for testing without executing main
if [ -z "${OPENELF_TEST:-}" ]; then
    main
fi
