#!/usr/bin/env bash
# nanx installer - https://install.nanx.dev
# Usage: curl -fsSL https://install.nanx.dev | bash
#    or: curl -fsSL https://install.nanx.dev | zsh
#    or: wget -qO- https://install.nanx.dev | bash
#
# Environment variables:
#   NANX_INSTALL_DIR - Override installation directory (default: ~/.local/bin or existing location)
#   NANX_FORCE=1     - Force reinstall even if same version is already installed

set -euo pipefail

# Configuration
NANX_DOWNLOAD_BASE="https://download.nanx.dev"
NANX_MANIFEST_URL="${NANX_DOWNLOAD_BASE}/latest/manifest.json"
DEFAULT_INSTALL_DIR="$HOME/.local/bin"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# Helper functions using printf for cross-shell compatibility
info() {
    printf "${BLUE}==>${NC} %b\n" "$1"
}

success() {
    printf "${GREEN}✓${NC} %b\n" "$1"
}

warn() {
    printf "${YELLOW}!${NC} %b\n" "$1"
}

error() {
    printf "${RED}✗${NC} %b\n" "$1" >&2
}

# Tracking configuration
NANX_TRACK_ENDPOINT="https://nanx.dev/api/e"
NANX_MACHINE_ID_FILE="$HOME/.surkyl/nanx/.machine_id"

# Get or create machine ID
get_machine_id() {
    local machine_id=""
    
    # Try to read existing ID
    if [ -f "$NANX_MACHINE_ID_FILE" ]; then
        machine_id=$(cat "$NANX_MACHINE_ID_FILE" 2>/dev/null)
    fi
    
    # Generate new ID if none exists
    if [ -z "$machine_id" ]; then
        # Create directory if needed
        mkdir -p "$(dirname "$NANX_MACHINE_ID_FILE")" 2>/dev/null || true
        
        # Generate UUID (cross-platform)
        if command -v uuidgen >/dev/null 2>&1; then
            machine_id=$(uuidgen)
        elif [ -f /proc/sys/kernel/random/uuid ]; then
            machine_id=$(cat /proc/sys/kernel/random/uuid)
        else
            machine_id="install-$(date +%s)-$$"
        fi
        
        # Save for future use
        printf "%s" "$machine_id" > "$NANX_MACHINE_ID_FILE" 2>/dev/null || true
    fi
    
    printf "%s" "$machine_id"
}

# Track event via nanx-dev API
track_event() {
    local event="$1"
    shift
    
    # Check opt-out
    if [ "${NANX_DISABLE_TELEMETRY:-}" = "1" ]; then
        return 0
    fi
    
    # Get machine ID
    local machine_id
    machine_id=$(get_machine_id)
    
    # Build JSON payload (manual to avoid jq dependency)
    local properties=""
    while [ $# -gt 0 ]; do
        if [ -n "$properties" ]; then
            properties="$properties,"
        fi
        # Escape quotes in values
        local key="$1"
        local value="$(printf '%s' "$2" | sed 's/"/\\"/g')"
        properties="$properties\"$key\":\"$value\""
        shift 2
    done
    
    local payload="{\"event\":\"$event\",\"distinct_id\":\"$machine_id\",\"properties\":{$properties}}"
    
    # Send async (don't block install)
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL "$NANX_TRACK_ENDPOINT" \
            -H "Content-Type: application/json" \
            -d "$payload" >/dev/null 2>&1 &
    elif command -v wget >/dev/null 2>&1; then
        wget -qO- --post-data="$payload" \
            --header="Content-Type: application/json" \
            "$NANX_TRACK_ENDPOINT" >/dev/null 2>&1 &
    fi
}

# Detect platform
detect_platform() {
    local os arch
    os="$(uname -s | tr '[:upper:]' '[:lower:]')"
    arch="$(uname -m)"

    case "$os" in
        linux) os="linux" ;;
        darwin) os="darwin" ;;
        *)
            error "Unsupported OS: $os"
            error "nanx currently supports Linux and macOS only."
            exit 1
            ;;
    esac

    case "$arch" in
        x86_64|amd64) arch="x86_64" ;;
        aarch64|arm64) arch="arm64" ;;
        *)
            error "Unsupported architecture: $arch"
            error "nanx currently supports x86_64 and ARM64 only."
            exit 1
            ;;
    esac

    # Detect Rosetta 2 on macOS (ARM binary on Intel Mac)
    if [ "$os" = "darwin" ] && [ "$arch" = "x86_64" ]; then
        local rosetta_flag
        rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
        if [ "$rosetta_flag" = "1" ]; then
            arch="arm64"
        fi
    fi

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

# Detect existing nanx installation directory
detect_existing_install() {
    local existing_path
    existing_path=$(command -v nanx 2>/dev/null) || true
    
    if [ -n "$existing_path" ]; then
        # Return the directory containing the binary
        dirname "$existing_path"
    else
        printf ""
    fi
}

# Get current installed version
get_installed_version() {
    if command -v nanx >/dev/null 2>&1; then
        nanx --version 2>/dev/null | awk '{print $2}' || printf "unknown"
    else
        printf ""
    fi
}

# Download with curl or wget (with progress bar if supported)
download() {
    local url="$1" output="$2" show_progress="${3:-false}"
    
    if command -v curl >/dev/null 2>&1; then
        if [ "$show_progress" = "true" ] && [ -t 2 ]; then
            # Show progress bar for interactive terminals
            curl -fL --progress-bar "$url" -o "$output" 2>&1 || {
                error "Download failed: $url"
                return 1
            }
        else
            # Silent download for non-interactive or when progress is disabled
            curl -fsSL "$url" -o "$output" || {
                error "Download failed: $url"
                return 1
            }
        fi
    elif command -v wget >/dev/null 2>&1; then
        if [ "$show_progress" = "true" ] && [ -t 2 ]; then
            # Show progress bar for interactive terminals
            wget --show-progress -q "$url" -O "$output" || {
                error "Download failed: $url"
                return 1
            }
        else
            # Silent download
            wget -qO "$output" "$url" || {
                error "Download failed: $url"
                return 1
            }
        fi
    else
        error "Neither curl nor wget found. Please install one of them."
        exit 1
    fi
}

# Get JSON value (works without jq)
json_value() {
    local json="$1" key="$2"
    printf "%s" "$json" | grep -o "\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | sed 's/.*: *"\([^"]*\)".*/\1/'
}

# Check if directory is writable (or can be created)
is_writable() {
    local dir="$1"
    if [ -d "$dir" ]; then
        [ -w "$dir" ]
    else
        # Check if parent is writable (to create the dir)
        local parent
        parent=$(dirname "$dir")
        [ -w "$parent" ] || [ "$parent" = "$HOME" ]
    fi
}

# Install binary with automatic sudo escalation if needed
install_binary() {
    local src="$1"
    local dest_dir="$2"
    local dest="$dest_dir/nanx"
    local use_sudo=false

    # Check if we need sudo
    if ! is_writable "$dest_dir"; then
        use_sudo=true
        warn "Elevated permissions required for ${CYAN}$dest_dir${NC}"
        info "Requesting sudo access..."
        
        # Validate sudo access
        if ! sudo -v 2>/dev/null; then
            error "Failed to obtain sudo permissions"
            error "Try installing to your home directory instead:"
            error "  NANX_INSTALL_DIR=~/.local/bin curl -fsSL https://install.nanx.dev | bash"
            return 1
        fi
    fi

    # Create directory if needed
    if [ ! -d "$dest_dir" ]; then
        if [ "$use_sudo" = true ]; then
            sudo mkdir -p "$dest_dir" || {
                error "Failed to create directory: $dest_dir"
                return 1
            }
        else
            mkdir -p "$dest_dir" || {
                error "Failed to create directory: $dest_dir"
                return 1
            }
        fi
    fi

    # Backup existing binary if present
    if [ -f "$dest" ]; then
        if [ "$use_sudo" = true ]; then
            sudo cp "$dest" "${dest}.old" 2>/dev/null || true
        else
            cp "$dest" "${dest}.old" 2>/dev/null || true
        fi
    fi

    # Install the binary
    if [ "$use_sudo" = true ]; then
        sudo mv "$src" "$dest" || {
            error "Failed to install binary to $dest_dir"
            return 1
        }
        # Set proper ownership to root (mv preserves original owner from /tmp)
        # This ensures nanx self-update correctly detects elevated permissions are needed
        sudo chown root:root "$dest" 2>/dev/null || true
        sudo chmod +x "$dest" || {
            warn "Failed to set executable permissions"
        }
    else
        mv "$src" "$dest" || {
            error "Failed to install binary to $dest_dir"
            return 1
        }
        chmod +x "$dest" || {
            warn "Failed to set executable permissions"
        }
    fi

    return 0
}

main() {
    printf "\n"
    printf "${BOLD}${CYAN}nanx installer${NC}\n"
    printf "\n"

    # Ensure HOME is set
    if [ -z "${HOME:-}" ]; then
        error "HOME environment variable is not set"
        exit 1
    fi

    # Check required commands
    if ! command -v tar >/dev/null 2>&1; then
        error "'tar' is required but not installed"
        error "Please install tar and try again"
        exit 1
    fi

    # Detect platform
    local platform
    platform=$(detect_platform)
    info "Detected platform: ${CYAN}$platform${NC}"

    # Track install started
    track_event "nanx_install_started" "platform" "$platform"

    # Create temp directory
    local tmpdir
    tmpdir=$(mktemp -d -t nanx-install.XXXXXXXXXX) || {
        error "Failed to create temporary directory"
        exit 1
    }
    
    # Setup cleanup trap - expand tmpdir now to avoid variable issues
    trap "rm -rf '$tmpdir' 2>/dev/null || true" EXIT INT TERM

    # Fetch manifest
    info "Fetching latest version info..."
    download "$NANX_MANIFEST_URL" "$tmpdir/manifest.json" || {
        error "Failed to fetch release manifest"
        error "Please check your internet connection and try again."
        exit 1
    }

    local manifest
    manifest=$(cat "$tmpdir/manifest.json")

    local version
    version=$(json_value "$manifest" "version")
    
    if [ -z "$version" ]; then
        error "Failed to parse version from manifest"
        exit 1
    fi

    info "Latest version: ${GREEN}$version${NC}"

    # Detect existing installation
    local existing_dir current_version install_dir
    existing_dir=$(detect_existing_install) || existing_dir=""
    current_version=$(get_installed_version) || current_version=""
    
    # Determine install directory (priority: NANX_INSTALL_DIR > existing location > PATH directory > default)
    if [ -n "${NANX_INSTALL_DIR:-}" ]; then
        install_dir="$NANX_INSTALL_DIR"
        info "Using specified install directory: ${CYAN}$install_dir${NC}"
    elif [ -n "$existing_dir" ]; then
        install_dir="$existing_dir"
        info "Found existing installation at: ${CYAN}$install_dir${NC}"
    else
        # Check for writable directories already in PATH
        install_dir=""
        for dir in "$HOME/.local/bin" "$HOME/bin" "/usr/local/bin"; do
            if [[ ":$PATH:" == *":$dir:"* ]] && is_writable "$dir"; then
                install_dir="$dir"
                break
            fi
        done
        
        # Fallback to default if no suitable directory found
        if [ -z "$install_dir" ]; then
            install_dir="$DEFAULT_INSTALL_DIR"
        fi
    fi

    # Check if already installed with same version
    if [ -n "$current_version" ] && [ "$current_version" != "unknown" ]; then
        if [ "$current_version" = "$version" ]; then
            if [ "${NANX_FORCE:-}" = "1" ]; then
                warn "nanx ${CYAN}$current_version${NC} is already installed (forcing reinstall)"
            else
                printf "\n"
                success "nanx ${GREEN}$version${NC} is already installed and up to date!"
                printf "\n"
                printf "Run ${CYAN}nanx --help${NC} to get started\n"
                printf "Run ${CYAN}nanx self update${NC} to check for updates anytime\n"
                printf "\n"
                printf "To force reinstall: ${CYAN}NANX_FORCE=1 curl -fsSL https://install.nanx.dev | bash${NC}\n"
                printf "\n"
                exit 0
            fi
        else
            info "Updating nanx from ${YELLOW}$current_version${NC} to ${GREEN}$version${NC}"
        fi
    fi

    # Get download URL for platform
    local download_url
    download_url="${NANX_DOWNLOAD_BASE}/latest/${platform}/nanx.tar.gz"

    # Download binary
    info "Downloading nanx ${GREEN}$version${NC} for ${CYAN}$platform${NC}..."
    download "$download_url" "$tmpdir/nanx.tar.gz" true || {
        error "Failed to download nanx binary"
        error "URL: $download_url"
        error ""
        error "Possible causes:"
        error "  - Network connection issue"
        error "  - Platform $platform not available for this version"
        error "  - Release not yet published"
        track_event "nanx_install_failed" "platform" "$platform" "step" "download" "error" "Failed to download binary"
        exit 1
    }

    # Download checksum
    info "Downloading checksum..."
    download "${download_url}.sha256" "$tmpdir/nanx.tar.gz.sha256" || {
        warn "Could not download checksum file. Skipping verification."
    }

    # Verify checksum (if available)
    if [ -f "$tmpdir/nanx.tar.gz.sha256" ]; then
        info "Verifying checksum..."
        
        # Extract just the hash from the checksum file (ignore filename mismatches)
        local expected_hash
        expected_hash=$(awk '{print $1}' "$tmpdir/nanx.tar.gz.sha256")
        
        # Calculate actual hash
        local actual_hash
        if command -v sha256sum >/dev/null 2>&1; then
            actual_hash=$(sha256sum "$tmpdir/nanx.tar.gz" | awk '{print $1}')
        elif command -v shasum >/dev/null 2>&1; then
            actual_hash=$(shasum -a 256 "$tmpdir/nanx.tar.gz" | awk '{print $1}')
        else
            warn "Cannot verify checksum (sha256sum/shasum not found)"
        fi
        
        # Compare hashes directly (ignore filename in checksum file)
        if [ -n "$expected_hash" ] && [ -n "$actual_hash" ] && [ "$expected_hash" != "$actual_hash" ]; then
            error "Checksum verification failed!"
            error "Expected: $expected_hash"
            error "Got:      $actual_hash"
            error "The download may be corrupted. Please try again."
            exit 1
        fi
        
        if [ -n "$actual_hash" ]; then
            success "Checksum verified"
        else
            warn "Checksum verification skipped"
        fi
    fi

    # Extract
    info "Extracting..."
    tar -xzf "$tmpdir/nanx.tar.gz" -C "$tmpdir" || {
        error "Failed to extract tarball"
        track_event "nanx_install_failed" "platform" "$platform" "step" "extract" "error" "Failed to extract tarball"
        exit 1
    }

    # Install
    info "Installing to ${CYAN}$install_dir${NC}..."
    install_binary "$tmpdir/nanx" "$install_dir" || {
        track_event "nanx_install_failed" "platform" "$platform" "step" "install" "error" "Failed to install binary"
        exit 1
    }

    printf "\n"
    if [ -n "$current_version" ] && [ "$current_version" != "$version" ]; then
        success "nanx updated from ${YELLOW}$current_version${NC} to ${GREEN}$version${NC}!"
    else
        success "nanx ${GREEN}$version${NC} installed successfully!"
    fi
    printf "\n"

    # Track install completed
    track_event "nanx_install_completed" \
        "platform" "$platform" \
        "version" "$version" \
        "install_dir" "$install_dir" \
        "is_update" "$([ -n "$current_version" ] && [ "$current_version" != "$version" ] && echo "true" || echo "false")" \
        "previous_version" "${current_version:-none}"

    # Check if in PATH and auto-add if needed
    if [[ ":$PATH:" != *":$install_dir:"* ]]; then
        setup_path "$install_dir"
    else
        printf "Run ${CYAN}nanx --help${NC} to get started\n"
        printf "Run ${CYAN}nanx --aliases${NC} to see all shortcuts\n"
        printf "\n"
        printf "Update anytime with: ${CYAN}nanx self update${NC}\n"
    fi
    printf "\n"
    
    # Auto-install shell completions
    install_completions "$install_dir"
}

# Install shell completions automatically
install_completions() {
    local install_dir="$1"
    local nanx_bin="$install_dir/nanx"
    
    # Only attempt if nanx is executable
    if [ ! -x "$nanx_bin" ]; then
        return 0
    fi
    
    info "Installing shell completions..."
    
    # Run nanx self install-autocomplete with force flag
    # The nanx command now auto-configures ~/.zshrc for zsh users
    if "$nanx_bin" self install-autocomplete --force 2>&1 | grep -q "auto"; then
        success "Shell completions installed and configured"
        printf "  ${GREEN}✓${NC} Your shell config has been updated automatically\n"
    elif "$nanx_bin" self install-autocomplete --force >/dev/null 2>&1; then
        success "Shell completions installed"
        
        # Show brief instructions based on shell
        local current_shell
        current_shell=$(basename "${SHELL:-bash}")
        
        case "$current_shell" in
            zsh)
                # Check if already configured
                if grep -q "/.zfunc" "$HOME/.zshrc" 2>/dev/null; then
                    printf "  ${GREEN}✓${NC} ~/.zshrc already configured\n"
                else
                    printf "  Add to ${CYAN}~/.zshrc${NC}: ${YELLOW}fpath=(~/.zfunc \$fpath)${NC}\n"
                fi
                ;;
            bash)
                printf "  Source the completion file in ${CYAN}~/.bashrc${NC}\n"
                ;;
        esac
    else
        # Silently skip if completions fail (non-critical)
        warn "Could not install shell completions automatically"
        printf "  Run manually: ${CYAN}nanx self install-autocomplete${NC}\n"
    fi
}

# Add directory to PATH in shell config
add_to_path() {
    local config_file="$1"
    local export_command="$2"
    
    if [ ! -f "$config_file" ]; then
        return 1
    fi
    
    # Check if already in config
    if grep -Fq "$export_command" "$config_file" 2>/dev/null; then
        return 0
    fi
    
    # Add to config file
    if [ -w "$config_file" ]; then
        printf "\n# nanx\n%s\n" "$export_command" >> "$config_file"
        return 0
    else
        return 1
    fi
}

# Setup PATH for the current shell
setup_path() {
    local install_dir="$1"
    local current_shell
    current_shell=$(basename "${SHELL:-bash}")
    
    local config_file=""
    local export_command="export PATH=\"\$PATH:$install_dir\""
    
    # Detect config file based on shell
    case "$current_shell" in
        zsh)
            for file in "$HOME/.zshrc" "$HOME/.zshenv" "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshrc"; do
                if [ -f "$file" ]; then
                    config_file="$file"
                    break
                fi
            done
            ;;
        bash)
            for file in "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
                if [ -f "$file" ]; then
                    config_file="$file"
                    break
                fi
            done
            ;;
        fish)
            config_file="$HOME/.config/fish/config.fish"
            export_command="fish_add_path $install_dir"
            ;;
        *)
            config_file="$HOME/.profile"
            ;;
    esac
    
    # Try to add to config file
    if [ -n "$config_file" ] && add_to_path "$config_file" "$export_command"; then
        success "Added ${CYAN}$install_dir${NC} to PATH in ${CYAN}$config_file${NC}"
        printf "\n"
        printf "Restart your shell or run:\n"
        printf "  ${CYAN}source $config_file${NC}\n"
        printf "\n"
        printf "Then run ${CYAN}nanx --help${NC} to get started\n"
    else
        # Fallback to manual instructions
        warn "$install_dir is not in your PATH"
        printf "\n"
        printf "Add the following to your shell profile:\n"
        printf "\n"
        printf "  ${CYAN}$export_command${NC}\n"
        printf "\n"
        if [ -n "$config_file" ]; then
            printf "Suggested file: ${CYAN}$config_file${NC}\n"
        else
            printf "Suggested file: ${CYAN}~/.bashrc${NC} or ${CYAN}~/.zshrc${NC}\n"
        fi
        printf "\n"
        printf "Then restart your shell or run:\n"
        printf "  ${CYAN}source ~/.bashrc${NC}  # or ~/.zshrc\n"
        printf "\n"
        printf "After that, run ${CYAN}nanx --help${NC} to get started\n"
    fi
}

main "$@"
