#!/bin/sh
set -eu

REPO_GIT_URL="git+https://github.com/Alishahryar1/free-claude-code.git"
PYTHON_VERSION="3.14.0"
MIN_UV_VERSION="0.11.0"
UV_INSTALL_URL="https://astral.sh/uv/install.sh"

dry_run=0
voice_nim=0
voice_local=0
voice_all=0
torch_backend=""

show_usage() {
    cat <<'USAGE'
Usage: install.sh [options]

Installs Claude Code if missing, installs or updates uv, Python 3.14.0, and Free Claude Code.

Options:
  --voice-nim              Install NVIDIA NIM voice transcription support.
  --voice-local            Install local Whisper voice transcription support.
  --voice-all              Install all voice transcription backends.
  --torch-backend VALUE    Use a uv PyTorch backend, such as cu130. Requires local voice.
  --dry-run                Print commands without running them.
  --help                   Show this help text.
USAGE
}

fail() {
    printf 'error: %s\n' "$*" >&2
    exit 1
}

step() {
    printf '\n==> %s\n' "$1"
}

quote_arg() {
    case "$1" in
        *[!A-Za-z0-9_./:@%+=,-]*|"")
            escaped=$(printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g')
            printf '"%s"' "$escaped"
            ;;
        *)
            printf '%s' "$1"
            ;;
    esac
}

print_command() {
    printf '+'
    for arg in "$@"; do
        printf ' '
        quote_arg "$arg"
    done
    printf '\n'
}

run() {
    print_command "$@"
    if [ "$dry_run" -eq 0 ]; then
        "$@"
    fi
}

run_uv_installer() {
    printf '+ curl -LsSf %s | sh\n' "$UV_INSTALL_URL"
    if [ "$dry_run" -eq 0 ]; then
        command -v curl >/dev/null 2>&1 || fail "curl is required to install uv."
        curl -LsSf "$UV_INSTALL_URL" | sh
    fi
}

add_path_entry() {
    [ -n "$1" ] || return 0
    case ":$PATH:" in
        *":$1:"*) ;;
        *) PATH="$1:$PATH" ;;
    esac
}

add_uv_to_path() {
    if [ -n "${XDG_BIN_HOME:-}" ]; then
        add_path_entry "$XDG_BIN_HOME"
    fi

    if [ -n "${HOME:-}" ]; then
        add_path_entry "$HOME/.local/bin"
        add_path_entry "$HOME/.cargo/bin"
    fi

    export PATH
}

require_command() {
    if [ "$dry_run" -eq 0 ] && ! command -v "$1" >/dev/null 2>&1; then
        fail "$1 is required. Install it first, then rerun this installer."
    fi
}

current_uv_version() {
    version=$(uv self version --short 2>/dev/null || true)
    if [ -z "$version" ]; then
        version=$(uv --version 2>/dev/null | sed 's/^uv //; s/ .*//' || true)
    fi

    [ -n "$version" ] || return 1
    printf '%s\n' "$version"
}

version_ge() {
    current=${1%%[-+]*}
    minimum=${2%%[-+]*}

    old_ifs=$IFS
    IFS=.
    set -- $current
    current_major=${1:-0}
    current_minor=${2:-0}
    current_patch=${3:-0}
    set -- $minimum
    minimum_major=${1:-0}
    minimum_minor=${2:-0}
    minimum_patch=${3:-0}
    IFS=$old_ifs

    [ "$current_major" -gt "$minimum_major" ] && return 0
    [ "$current_major" -lt "$minimum_major" ] && return 1
    [ "$current_minor" -gt "$minimum_minor" ] && return 0
    [ "$current_minor" -lt "$minimum_minor" ] && return 1
    [ "$current_patch" -ge "$minimum_patch" ]
}

uv_version_satisfies_minimum() {
    version=$(current_uv_version) || return 1
    version_ge "$version" "$MIN_UV_VERSION"
}

validate_uv_version() {
    [ "$dry_run" -eq 1 ] && return 0

    version=$(current_uv_version) || fail "Unable to determine uv version."
    if ! version_ge "$version" "$MIN_UV_VERSION"; then
        fail "uv $MIN_UV_VERSION or newer is required; found uv $version. Upgrade uv with its installer or package manager, then rerun this installer."
    fi
}

uv_self_update_supported() {
    uv self update --dry-run >/dev/null 2>&1
}

uv_installed_by_homebrew() {
    command -v brew >/dev/null 2>&1 && brew list --versions uv >/dev/null 2>&1
}

uv_installed_by_pipx() {
    command -v pipx >/dev/null 2>&1 && pipx list 2>/dev/null | grep -Eq '(^|[[:space:]])package uv([[:space:]]|$)'
}

uv_installed_in_active_virtualenv() {
    [ -n "${VIRTUAL_ENV:-}" ] || return 1

    uv_path=$(command -v uv)
    case "$uv_path" in
        "$VIRTUAL_ENV"/*) return 0 ;;
        *) return 1 ;;
    esac
}

update_existing_uv() {
    if uv_self_update_supported; then
        run uv self update
        return 0
    fi

    if uv_installed_by_homebrew; then
        run brew upgrade uv
        return 0
    fi

    if uv_installed_by_pipx; then
        run pipx upgrade uv
        return 0
    fi

    if uv_installed_in_active_virtualenv; then
        run python -m pip install --upgrade uv
        return 0
    fi

    if uv_version_satisfies_minimum; then
        printf 'uv is already installed and satisfies >=%s; skipping automatic uv update because the install source was not detected.\n' "$MIN_UV_VERSION"
        return 0
    fi

    version=$(current_uv_version 2>/dev/null || printf 'unknown')
    fail "uv $MIN_UV_VERSION or newer is required; found uv $version. The existing uv install source was not detected. Upgrade uv manually with the package manager that installed it, then rerun this installer."
}

install_claude_if_missing() {
    if command -v claude >/dev/null 2>&1; then
        printf 'Claude Code already found on PATH; skipping install.\n'
        return 0
    fi

    require_command npm
    run npm install -g @anthropic-ai/claude-code
}

install_or_update_uv() {
    add_uv_to_path

    if command -v uv >/dev/null 2>&1; then
        update_existing_uv
        validate_uv_version
        return 0
    fi

    run_uv_installer
    add_uv_to_path

    if [ "$dry_run" -eq 0 ] && ! command -v uv >/dev/null 2>&1; then
        fail "uv was installed, but it is not available on PATH. Open a new terminal or add uv's bin directory to PATH."
    fi

    validate_uv_version
}

parse_args() {
    while [ "$#" -gt 0 ]; do
        case "$1" in
            --voice-nim)
                voice_nim=1
                ;;
            --voice-local)
                voice_local=1
                ;;
            --voice-all)
                voice_all=1
                ;;
            --torch-backend)
                shift
                [ "$#" -gt 0 ] || fail "--torch-backend requires a value."
                torch_backend="$1"
                [ -n "$torch_backend" ] || fail "--torch-backend requires a non-empty value."
                ;;
            --torch-backend=*)
                torch_backend="${1#*=}"
                [ -n "$torch_backend" ] || fail "--torch-backend requires a non-empty value."
                ;;
            --dry-run)
                dry_run=1
                ;;
            --help|-h)
                show_usage
                exit 0
                ;;
            *)
                show_usage >&2
                fail "unknown option: $1"
                ;;
        esac
        shift
    done
}

validate_args() {
    include_local=$voice_local

    if [ "$voice_all" -eq 1 ]; then
        include_local=1
    fi

    if [ -n "$torch_backend" ] && [ "$include_local" -ne 1 ]; then
        fail "--torch-backend requires --voice-local or --voice-all."
    fi
}

package_spec() {
    include_nim=$voice_nim
    include_local=$voice_local

    if [ "$voice_all" -eq 1 ]; then
        include_nim=1
        include_local=1
    fi

    if [ -n "$torch_backend" ] && [ "$include_local" -ne 1 ]; then
        fail "--torch-backend requires --voice-local or --voice-all."
    fi

    if [ "$include_nim" -eq 1 ] && [ "$include_local" -eq 1 ]; then
        printf 'free-claude-code[voice,voice_local] @ %s' "$REPO_GIT_URL"
    elif [ "$include_nim" -eq 1 ]; then
        printf 'free-claude-code[voice] @ %s' "$REPO_GIT_URL"
    elif [ "$include_local" -eq 1 ]; then
        printf 'free-claude-code[voice_local] @ %s' "$REPO_GIT_URL"
    else
        printf '%s' "$REPO_GIT_URL"
    fi
}

install_free_claude_code() {
    spec=$(package_spec)

    if [ -n "$torch_backend" ]; then
        run uv tool install --force --torch-backend "$torch_backend" "$spec"
    else
        run uv tool install --force "$spec"
    fi
}

parse_args "$@"
validate_args

step "Installing Claude Code if missing"
install_claude_if_missing

step "Installing uv if missing, updating if present"
install_or_update_uv

step "Installing Python $PYTHON_VERSION"
run uv python install "$PYTHON_VERSION"

step "Installing or updating Free Claude Code"
install_free_claude_code

printf '\nFree Claude Code is installed. Start the proxy with: fcc-server\n'
