#!/usr/bin/env bash
#
# GDELT CLI Installer
#
# This script installs the gdelt CLI tool by:
# 1. Attempting to download a pre-built binary from GitHub releases
# 2. Falling back to compiling from source if no binary is available
#
# Usage:
#   curl -fsSL https://raw.githubusercontent.com/dipankar/gdelt-cli/main/install.sh | bash
#   # or
#   ./install.sh
#
# Options:
#   GDELT_INSTALL_DIR  - Installation directory (default: ~/.local/bin or /usr/local/bin)
#   GDELT_VERSION      - Specific version to install (default: latest)
#   GDELT_NO_MODIFY_PATH - Set to 1 to skip PATH modification

set -euo pipefail

# Configuration
REPO="dipankar/gdelt-cli"
BINARY_NAME="gdelt"
INSTALL_DIR="${GDELT_INSTALL_DIR:-}"
VERSION="${GDELT_VERSION:-latest}"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Logging functions
info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

success() {
    echo -e "${GREEN}[OK]${NC} $1"
}

warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

die() {
    error "$1"
    exit 1
}

# Detect OS and architecture
detect_platform() {
    local os arch

    # Detect OS
    case "$(uname -s)" in
        Linux*)     os="linux";;
        Darwin*)    os="darwin";;
        MINGW*|MSYS*|CYGWIN*) os="windows";;
        *)          die "Unsupported operating system: $(uname -s)";;
    esac

    # Detect architecture
    case "$(uname -m)" in
        x86_64|amd64)   arch="x86_64";;
        aarch64|arm64)  arch="aarch64";;
        armv7l)         arch="armv7";;
        *)              die "Unsupported architecture: $(uname -m)";;
    esac

    echo "${os}-${arch}"
}

# Get the latest release version from GitHub
get_latest_version() {
    local version
    version=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null | \
        grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' || echo "")

    if [[ -z "$version" ]]; then
        return 1
    fi
    echo "$version"
}

# Determine installation directory
determine_install_dir() {
    if [[ -n "$INSTALL_DIR" ]]; then
        echo "$INSTALL_DIR"
        return
    fi

    # Check if we can write to /usr/local/bin
    if [[ -w "/usr/local/bin" ]]; then
        echo "/usr/local/bin"
    else
        # Fall back to user's local bin
        local user_bin="${HOME}/.local/bin"
        mkdir -p "$user_bin"
        echo "$user_bin"
    fi
}

# Check if a command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Download and install from GitHub releases
install_from_release() {
    local platform="$1"
    local install_dir="$2"
    local version="$3"

    info "Attempting to download pre-built binary..."

    # Construct download URL
    local os arch ext
    os=$(echo "$platform" | cut -d'-' -f1)
    arch=$(echo "$platform" | cut -d'-' -f2)

    if [[ "$os" == "windows" ]]; then
        ext=".exe"
    else
        ext=""
    fi

    # Try different naming conventions
    local urls=(
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}-${version}-${platform}${ext}"
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}-${platform}${ext}"
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}-${os}-${arch}${ext}"
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}${ext}"
    )

    # Also try tar.gz archives
    local archive_urls=(
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}-${version}-${platform}.tar.gz"
        "https://github.com/${REPO}/releases/download/${version}/${BINARY_NAME}-${platform}.tar.gz"
    )

    local tmp_dir
    tmp_dir=$(mktemp -d)
    trap "rm -rf $tmp_dir" EXIT

    # Try direct binary downloads
    for url in "${urls[@]}"; do
        info "Trying: $url"
        if curl -fsSL "$url" -o "$tmp_dir/${BINARY_NAME}${ext}" 2>/dev/null; then
            chmod +x "$tmp_dir/${BINARY_NAME}${ext}"
            mv "$tmp_dir/${BINARY_NAME}${ext}" "${install_dir}/${BINARY_NAME}${ext}"
            success "Downloaded binary from release"
            return 0
        fi
    done

    # Try archive downloads
    for url in "${archive_urls[@]}"; do
        info "Trying archive: $url"
        if curl -fsSL "$url" -o "$tmp_dir/archive.tar.gz" 2>/dev/null; then
            tar -xzf "$tmp_dir/archive.tar.gz" -C "$tmp_dir"
            if [[ -f "$tmp_dir/${BINARY_NAME}${ext}" ]]; then
                chmod +x "$tmp_dir/${BINARY_NAME}${ext}"
                mv "$tmp_dir/${BINARY_NAME}${ext}" "${install_dir}/${BINARY_NAME}${ext}"
                success "Downloaded and extracted binary from release"
                return 0
            fi
            # Try to find binary in extracted files
            local found_binary
            found_binary=$(find "$tmp_dir" -name "${BINARY_NAME}${ext}" -type f 2>/dev/null | head -1)
            if [[ -n "$found_binary" ]]; then
                chmod +x "$found_binary"
                mv "$found_binary" "${install_dir}/${BINARY_NAME}${ext}"
                success "Downloaded and extracted binary from release"
                return 0
            fi
        fi
    done

    warn "No pre-built binary found for ${platform}"
    return 1
}

# Install from source using cargo
install_from_source() {
    local install_dir="$1"

    info "Installing from source..."

    # Check for Rust/cargo
    if ! command_exists cargo; then
        warn "Cargo not found. Installing Rust..."
        install_rust
    fi

    # Check if we're in the repo directory
    if [[ -f "Cargo.toml" ]] && grep -q 'name = "gdelt"' Cargo.toml 2>/dev/null; then
        info "Building from local source..."
        cargo build --release

        local binary_path="target/release/${BINARY_NAME}"
        if [[ -f "$binary_path" ]]; then
            cp "$binary_path" "${install_dir}/${BINARY_NAME}"
            chmod +x "${install_dir}/${BINARY_NAME}"
            success "Built and installed from local source"
            return 0
        fi
    fi

    # Clone and build from GitHub
    local tmp_dir
    tmp_dir=$(mktemp -d)
    trap "rm -rf $tmp_dir" EXIT

    info "Cloning repository..."
    if ! git clone --depth 1 "https://github.com/${REPO}.git" "$tmp_dir/gdelt-cli" 2>/dev/null; then
        die "Failed to clone repository"
    fi

    cd "$tmp_dir/gdelt-cli"

    info "Building with cargo (this may take a few minutes)..."
    if ! cargo build --release; then
        die "Failed to build from source"
    fi

    local binary_path="target/release/${BINARY_NAME}"
    if [[ ! -f "$binary_path" ]]; then
        die "Binary not found after build"
    fi

    cp "$binary_path" "${install_dir}/${BINARY_NAME}"
    chmod +x "${install_dir}/${BINARY_NAME}"
    success "Built and installed from source"
}

# Install Rust using rustup
install_rust() {
    info "Installing Rust via rustup..."

    if ! curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; then
        die "Failed to install Rust"
    fi

    # Source cargo environment
    # shellcheck source=/dev/null
    source "${HOME}/.cargo/env" 2>/dev/null || true

    if ! command_exists cargo; then
        die "Cargo still not found after Rust installation"
    fi

    success "Rust installed successfully"
}

# Add directory to PATH in shell config
add_to_path() {
    local dir="$1"

    # Check if already in PATH
    if [[ ":$PATH:" == *":$dir:"* ]]; then
        return 0
    fi

    # Skip if user requested no PATH modification
    if [[ "${GDELT_NO_MODIFY_PATH:-}" == "1" ]]; then
        return 0
    fi

    local shell_config=""
    local shell_name
    shell_name=$(basename "$SHELL")

    case "$shell_name" in
        bash)
            if [[ -f "${HOME}/.bashrc" ]]; then
                shell_config="${HOME}/.bashrc"
            elif [[ -f "${HOME}/.bash_profile" ]]; then
                shell_config="${HOME}/.bash_profile"
            fi
            ;;
        zsh)
            shell_config="${HOME}/.zshrc"
            ;;
        fish)
            shell_config="${HOME}/.config/fish/config.fish"
            ;;
    esac

    if [[ -n "$shell_config" ]]; then
        local path_line="export PATH=\"\$PATH:$dir\""
        if [[ "$shell_name" == "fish" ]]; then
            path_line="set -gx PATH \$PATH $dir"
        fi

        if ! grep -q "$dir" "$shell_config" 2>/dev/null; then
            echo "" >> "$shell_config"
            echo "# Added by gdelt installer" >> "$shell_config"
            echo "$path_line" >> "$shell_config"
            info "Added $dir to PATH in $shell_config"
            warn "Run 'source $shell_config' or restart your shell to update PATH"
        fi
    fi
}

# Print post-install instructions
print_instructions() {
    local install_dir="$1"

    echo ""
    echo "============================================"
    success "GDELT CLI installed successfully!"
    echo "============================================"
    echo ""
    echo "Binary location: ${install_dir}/${BINARY_NAME}"
    echo ""
    echo "Quick start:"
    echo "  gdelt --help              # Show help"
    echo "  gdelt doc search 'query'  # Search news articles"
    echo "  gdelt events query        # Query local events"
    echo "  gdelt schema commands     # List all commands"
    echo ""

    # Check if in PATH
    if ! command_exists gdelt; then
        warn "The installation directory is not in your PATH."
        echo ""
        echo "Add it manually:"
        echo "  export PATH=\"\$PATH:${install_dir}\""
        echo ""
        echo "Or add to your shell config (~/.bashrc, ~/.zshrc, etc.)"
    fi
}

# Verify installation
verify_installation() {
    local install_dir="$1"
    local binary="${install_dir}/${BINARY_NAME}"

    if [[ ! -f "$binary" ]]; then
        die "Installation verification failed: binary not found"
    fi

    if [[ ! -x "$binary" ]]; then
        die "Installation verification failed: binary not executable"
    fi

    # Try to run the binary
    if "$binary" --version >/dev/null 2>&1; then
        success "Installation verified"
    else
        warn "Binary installed but version check failed"
    fi
}

# Main installation flow
main() {
    echo ""
    echo "=========================================="
    echo "       GDELT CLI Installer"
    echo "=========================================="
    echo ""

    # Check for required tools
    if ! command_exists curl; then
        die "curl is required but not installed"
    fi

    # Detect platform
    local platform
    platform=$(detect_platform)
    info "Detected platform: $platform"

    # Get version
    local version="$VERSION"
    if [[ "$version" == "latest" ]]; then
        info "Fetching latest version..."
        version=$(get_latest_version) || version=""
        if [[ -z "$version" ]]; then
            warn "Could not determine latest version, will build from source"
        else
            info "Latest version: $version"
        fi
    fi

    # Determine installation directory
    local install_dir
    install_dir=$(determine_install_dir)
    info "Installation directory: $install_dir"

    # Create installation directory if needed
    mkdir -p "$install_dir"

    # Try to install from release first
    local installed=false
    if [[ -n "$version" ]]; then
        if install_from_release "$platform" "$install_dir" "$version"; then
            installed=true
        fi
    fi

    # Fall back to source installation
    if [[ "$installed" != "true" ]]; then
        info "Falling back to source installation..."

        # Check for git
        if ! command_exists git; then
            die "git is required for source installation but not found"
        fi

        install_from_source "$install_dir"
    fi

    # Add to PATH if needed
    add_to_path "$install_dir"

    # Verify installation
    verify_installation "$install_dir"

    # Print instructions
    print_instructions "$install_dir"
}

# Run main
main "$@"
