#!/usr/bin/env bash
# =============================================================================
# iwant.sh — Fuzzy package finder & installer for common Linux distros
# To run: CHMOD +X iwant.sh && ./iwant.sh
# Supports: apt, dnf, yum, pacman, zypper, apk, xbps-install, emerge
# Usage: iwant.sh <search term>
#        iwant.sh "search term with spaces"
# =============================================================================

set -euo pipefail

# --- Colours -----------------------------------------------------------------
RED='\033[0;31m'
GRN='\033[0;32m'
YLW='\033[1;33m'
BLU='\033[0;34m'
CYN='\033[0;36m'
BLD='\033[1m'
RST='\033[0m'

# --- Helpers -----------------------------------------------------------------
die()  { echo -e "${RED}error:${RST} $*" >&2; exit 1; }
info() { echo -e "${BLU}::${RST} $*"; }
ok()   { echo -e "${GRN}✔${RST}  $*"; }
warn() { echo -e "${YLW}!${RST}  $*"; }

# Print a numbered menu from an array
print_menu() {
    local -n _items=$1
    local i=1
    for item in "${_items[@]}"; do
        printf "  ${CYN}%3d${RST}  %s\n" "$i" "$item"
        ((i++))
    done
}

# Prompt user to pick one item from a menu; sets CHOSEN
pick_from_menu() {
    local -n _pick_items=$1
    local count=${#_pick_items[@]}
    local choice

    while true; do
        printf "\n${BLD}Select a package [1-%d, or 0 to cancel]:${RST} " "$count"
        read -r choice
        if [[ "$choice" == "0" ]]; then
            warn "Cancelled."
            exit 0
        fi
        if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= count )); then
            CHOSEN="${_pick_items[$((choice - 1))]}"
            # Strip any trailing description (keep only the package name)
            CHOSEN=$(echo "$CHOSEN" | awk '{print $1}')
            return
        fi
        warn "Please enter a number between 1 and $count."
    done
}

# Confirm before installing
confirm_install() {
    local pkg="$1"
    local mgr="$2"
    printf "\n${BLD}Install ${GRN}%s${RST}${BLD} using %s?${RST} [y/N] " "$pkg" "$mgr"
    read -r yn
    [[ "${yn,,}" == "y" ]] || { warn "Aborted."; exit 0; }
}

# =============================================================================
# Package manager detection
# =============================================================================
detect_pm() {
    if   command -v apt-get   &>/dev/null; then PM="apt"
    elif command -v dnf       &>/dev/null; then PM="dnf"
    elif command -v yum       &>/dev/null; then PM="yum"
    elif command -v pacman    &>/dev/null; then PM="pacman"
    elif command -v zypper    &>/dev/null; then PM="zypper"
    elif command -v apk       &>/dev/null; then PM="apk"
    elif command -v xbps-install &>/dev/null; then PM="xbps"
    elif command -v emerge    &>/dev/null; then PM="emerge"
    else die "No supported package manager found on this system."
    fi
}

# =============================================================================
# Search functions — each returns results in RESULTS array
# Format: "package-name   short description"
# =============================================================================

search_apt() {
    # apt-cache search does full fuzzy/substring matching already
    mapfile -t RESULTS < <(
        apt-cache search "$QUERY" 2>/dev/null \
        | awk '{printf "%-35s %s\n", $1, substr($0, index($0,$2))}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_dnf() {
    mapfile -t RESULTS < <(
        dnf search "$QUERY" 2>/dev/null \
        | grep -v '^\(==\|Last\|Loaded\)' \
        | awk -F: '{gsub(/\.[^.]+$/, "", $1); printf "%-35s %s\n", $1, $2}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_yum() {
    mapfile -t RESULTS < <(
        yum search "$QUERY" 2>/dev/null \
        | grep ':' \
        | grep -v '^\(==\|Loaded\)' \
        | awk -F: '{gsub(/\.[^.]+$/, "", $1); printf "%-35s %s\n", $1, $2}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_pacman() {
    # pacman -Ss outputs two-line blocks: repo/name version \n description
    mapfile -t RESULTS < <(
        pacman -Ss "$QUERY" 2>/dev/null \
        | awk '
            /^\S/ { split($1, a, "/"); name=a[2]; next }
            { printf "%-35s %s\n", name, $0 }
        ' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_zypper() {
    mapfile -t RESULTS < <(
        zypper search "$QUERY" 2>/dev/null \
        | awk -F'|' 'NR>4 && $2~/[a-z]/ {gsub(/ /,"",$2); printf "%-35s %s\n", $2, $4}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_apk() {
    mapfile -t RESULTS < <(
        apk search "$QUERY" 2>/dev/null \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_xbps() {
    mapfile -t RESULTS < <(
        xbps-query -Rs "$QUERY" 2>/dev/null \
        | awk '{printf "%-35s %s\n", $2, substr($0, index($0,$3))}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

search_emerge() {
    mapfile -t RESULTS < <(
        emerge --search "$QUERY" 2>/dev/null \
        | awk '/^\*/{name=$2} /Description/{printf "%-35s %s\n", name, substr($0,14)}' \
        | grep -i "$QUERY" \
        | sort \
        | head -n 40
    )
}

# =============================================================================
# Install functions
# =============================================================================

install_apt()    { sudo apt-get install -y "$1"; }
install_dnf()    { sudo dnf install -y "$1"; }
install_yum()    { sudo yum install -y "$1"; }
install_pacman() { sudo pacman -S --noconfirm "$1"; }
install_zypper() { sudo zypper install -y "$1"; }
install_apk()    { sudo apk add "$1"; }
install_xbps()   { sudo xbps-install -S "$1"; }
install_emerge() { sudo emerge "$1"; }

# =============================================================================
# Main
# =============================================================================

# --- Usage check -------------------------------------------------------------
if [[ $# -lt 1 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
    echo -e "${BLD}iwant.sh${RST} — fuzzy package search & install"
    echo
    echo -e "  ${BLD}Usage:${RST}  iwant.sh <search term>"
    echo -e "  ${BLD}Example:${RST} iwant.sh neovim"
    echo
    exit 0
fi

QUERY="$*"           # join all args so "iwant.sh neovim editor" works
RESULTS=()
CHOSEN=""

# --- Detect package manager --------------------------------------------------
detect_pm
info "Detected package manager: ${BLD}${PM}${RST}"
info "Searching for ${BLD}${QUERY}${RST} ..."
echo

# --- Run the appropriate search ----------------------------------------------
case "$PM" in
    apt)    search_apt    ;;
    dnf)    search_dnf    ;;
    yum)    search_yum    ;;
    pacman) search_pacman ;;
    zypper) search_zypper ;;
    apk)    search_apk    ;;
    xbps)   search_xbps   ;;
    emerge) search_emerge  ;;
esac

# --- Handle no results -------------------------------------------------------
if [[ ${#RESULTS[@]} -eq 0 ]]; then
    warn "No packages found matching '${QUERY}'."
    warn "Check your spelling or try a broader search term."
    exit 1
fi

# --- Display results and pick one -------------------------------------------
echo -e "${BLD}Found ${#RESULTS[@]} result(s):${RST}\n"
print_menu RESULTS
pick_from_menu RESULTS

# --- Confirm and install -----------------------------------------------------
confirm_install "$CHOSEN" "$PM"
echo
info "Installing ${BLD}${CHOSEN}${RST} ..."
echo

case "$PM" in
    apt)    install_apt    "$CHOSEN" ;;
    dnf)    install_dnf    "$CHOSEN" ;;
    yum)    install_yum    "$CHOSEN" ;;
    pacman) install_pacman "$CHOSEN" ;;
    zypper) install_zypper "$CHOSEN" ;;
    apk)    install_apk    "$CHOSEN" ;;
    xbps)   install_xbps   "$CHOSEN" ;;
    emerge) install_emerge  "$CHOSEN" ;;
esac

echo
ok "${BLD}${CHOSEN}${RST} installed successfully."
