#!/usr/bin/env bash

#
# machine0 Installer
# curl -LsSf https://machine0.io/install.sh | sh
#
# This script will install:
# - Node.js (using the official nvm installer)
# - machine0 (using npm)
#
# Alternatively, you can install directly from npm:
# npm install -g @machine0/cli
#

# When run by a non-bash shell (e.g. dash via `curl … | sh` on Ubuntu),
# re-exec under bash. The rest of this script is bash-only.
if [ -z "${BASH_VERSION:-}" ]; then
    # Loop breaker: if we already re-exec'd once and still aren't in bash,
    # `bash` on this system is a shim for another shell (busybox ash, dash).
    if [ -n "${MACHINE0_INSTALL_REEXEC:-}" ]; then
        echo "Error: 'bash' on this system is not GNU bash. Install GNU bash and retry." >&2
        exit 1
    fi
    if ! command -v bash >/dev/null 2>&1; then
        echo "Error: bash is required to run this installer. Install bash and retry." >&2
        exit 1
    fi
    MACHINE0_INSTALL_REEXEC=1
    export MACHINE0_INSTALL_REEXEC
    # Invoked as a file (e.g. `sh ./install.sh`): re-run the same file so a
    # downloaded or locally modified copy is never swapped for the prod one.
    case "$0" in
        *install.sh) exec bash "$0" "$@" ;;
    esac
    # Piped (e.g. `curl … | sh`): the script only exists on this shell's
    # partially-consumed stdin, so re-fetch it and hand the full copy to bash.
    script="$(curl -LsSf --proto '=https' https://machine0.io/install.sh)" || {
        echo "Error: failed to download the installer." >&2
        exit 1
    }
    [ -n "$script" ] || {
        echo "Error: downloaded installer is empty." >&2
        exit 1
    }
    # Captive portals and intercepting proxies return HTTP 200 HTML pages.
    case "$script" in
        "#!/usr/bin/env bash"*) ;;
        *)
            echo "Error: downloaded installer is corrupted (are you behind a captive portal?)." >&2
            exit 1
            ;;
    esac
    # </dev/null matches `curl | bash` semantics: children see EOF on stdin
    # instead of the unread tail of the script left in the pipe.
    exec bash -c "$script" install.sh "$@" </dev/null
fi

# Exit immediately if any command fails and enable error tracing
set -o errexit
set -o pipefail

# ========================= CONFIGURATION =========================

# Progress Configuration
readonly TOTAL_STEPS=2

# Global Variables
current_step=0

# ========================= COLORS & STYLING =========================

# ANSI Color Codes with fallback support
if command -v tput &>/dev/null && [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
    # Rich colors using tput for better compatibility
    readonly RED=$(tput setaf 1)
    readonly GREEN=$(tput setaf 2)
    readonly YELLOW=$(tput setaf 3)
    readonly BLUE=$(tput setaf 4)
    readonly MAGENTA=$(tput setaf 5)
    readonly CYAN=$(tput setaf 6)
    readonly WHITE=$(tput setaf 7)
    readonly GRAY=$(tput setaf 8)
    readonly BRIGHT_GREEN=$(tput setaf 10)
    readonly BRIGHT_BLUE=$(tput setaf 12)
    readonly BRIGHT_CYAN=$(tput setaf 14)

    # Text Formatting
    readonly BOLD=$(tput bold)
    readonly DIM=$(tput dim)
    readonly ITALIC=$(tput sitm)
    readonly UNDERLINE=$(tput smul)
    readonly RESET=$(tput sgr0)
    readonly REVERSE=$(tput rev)

    # Background colors
    readonly BG_GREEN=$(tput setab 2)
    readonly BG_RED=$(tput setab 1)
    readonly BG_BLUE=$(tput setab 4)
else
    # Fallback for environments without color support
    readonly RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" WHITE="" GRAY=""
    readonly BRIGHT_GREEN="" BRIGHT_BLUE="" BRIGHT_CYAN=""
    readonly BOLD="" DIM="" ITALIC="" UNDERLINE="" RESET="" REVERSE=""
    readonly BG_GREEN="" BG_RED="" BG_BLUE=""
fi

# ========================= UNICODE SYMBOLS =========================

show_banner() {
    printf "\n"
    printf "  %s\n" "machine0 Installer"
    printf "  %s%s%s\n" "${DIM}" "Instant cloud VMs from your terminal" "${RESET}"
    printf "  %s%s%s\n" "${DIM}" "https://machine0.io" "${RESET}"
    printf "\n"
}

# ========================= PROGRESS VISUALIZATION =========================

show_progress() {
    local step_description="$1"

    if [ $current_step -eq 0 ]; then
        current_step=1
    else
        current_step=$((current_step + 1))
    fi

    # Simple progress indicator
    printf "  [%d/%d] %s" "$current_step" "$TOTAL_STEPS" "$step_description"
}


# Start a background process and show loader
run_command() {
    local message="$1"
    shift
    printf '%*s\n' "${COLUMNS:-$(tput cols 2>/dev/null || echo 80)}" '' | tr ' ' '━'
    echo "  $message"
    printf '%*s\n' "${COLUMNS:-$(tput cols 2>/dev/null || echo 80)}" '' | tr ' ' '━'

    "$@"
    return $?
}

# ========================= STATUS MESSAGES =========================

show_success() {
    printf "    %s✓ %s%s\n" "${DIM}" "$1" "${RESET}"
}

show_error() {
    printf "  ✗ Error: %s\n" "$1" >&2
    exit 1
}

show_warning() {
    printf "    ⚠ %s\n" "$1"
}

show_info() {
    printf "    %s• %s%s\n" "${DIM}" "$1" "${RESET}"
}

# ========================= ENHANCED UTILITIES =========================

command_exists() {
    command -v "$1" &>/dev/null
}

# ========================= OPTIONAL COMPONENTS =========================

install_nvm() {
    if command_exists nvm; then
        show_info "nvm is already installed. skipping..."
        return 0
    fi

    if [[ "$OSTYPE" == "darwin"* ]]; then
        # create a .zshrc file if it doesn't exist
        # on fresh osx this is needed for npm install
        touch $HOME/.zshrc
    fi

    run_command "installing nvm" bash -c "$(curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh)"
    source_nvm

    if ! command_exists nvm; then
        show_error "nvm is not available. Reload your terminal and try again."
        return 1
    fi
}


install_node() {
    # Try sourcing nvm.sh if it exists
    source_nvm

    if ! command_exists nvm; then
        install_nvm
    fi

    # doesn't work with run_command for some reason, just exits the whole script
    nvm install node --default
    source_nvm

    if ! command_exists node; then
        show_error "node.js is not available. Reload your terminal and try again."
        return 1
    fi
}

ensure_node_installed() {
    # Try sourcing nvm.sh if it exists
    source_nvm

    if command_exists node; then
        if command_exists npm; then
            show_success "node.js is already installed"
            return 0
        else
            show_error "node is installed but npm is not available in your \$PATH."
            return 1
        fi
    else
        install_node
        return 0
    fi
}


source_nvm() {
    if [ -f "$HOME/.nvm/nvm.sh" ]; then
        source "$HOME/.nvm/nvm.sh"
    fi
}


ensure_machine0_installed() {
    if command_exists machine0; then
        show_success "machine0 is already installed"
    else
        if command_exists npm; then
            if run_command "installing machine0..." npm install -g @machine0/cli; then
                show_success "machine0 is now installed"
                return 0
            else
                show_error "something went wrong."
                return 1
            fi
        else
            show_error "npm is not installed or available in your \$PATH."
            return 1
        fi
    fi
}


# ========================= MAIN INSTALLATION FLOW =========================

main() {
    show_banner

    # System compatibility check
    if [[ "$OSTYPE" != "darwin"* ]] && [[ "$OSTYPE" != "linux"* ]]; then
        show_error "This installer only supports macOS and Linux environments."
    fi

    # NixOS: nvm-installed Node won't work due to dynamic linker incompatibility
    if [ -f /etc/NIXOS ] || [ -d /etc/nixos ]; then
        if ! command_exists node; then
            show_error "NixOS detected but Node.js is not available. On NixOS, install Node.js via your configuration.nix or nix-env before running this script. Alternatively, use the nixos-25-11-loaded image which includes Node.js."
        fi
    fi

    # Ubuntu/Debian: Node.js v25+ requires libatomic1 which isn't installed by default
    if [ -f /etc/debian_version ] && ! ldconfig -p 2>/dev/null | grep -q libatomic; then
        if command_exists sudo; then
            sudo apt-get update -qq && sudo apt-get install -y -qq libatomic1
        elif [ "$(id -u)" -eq 0 ]; then
            apt-get update -qq && apt-get install -y -qq libatomic1
        fi
    fi

    show_progress "Installing Node.js"
    printf "\n"
    ensure_node_installed

    show_progress "Installing machine0"
    printf "\n"
    ensure_machine0_installed

    # Installation complete
    printf "\n"
    printf "  %s✓ installation complete%s" "${GREEN}" "${RESET}"
    printf "\n"

    printf "  %sRestart your terminal and run %s%smachine0 new my-vm%s%s to get started%s\n" "${DIM}" "${RESET}" "${BOLD}" "${RESET}" "${DIM}" "${RESET}"
    printf "\n"
}

main "$@"
