#!/usr/bin/env bash
# install-app — install executables (AppImage/ELF/script) to PATH with .desktop integration
# Usage: install-app [OPTIONS] <executable>

set -euo pipefail

# ── Colors ────────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GRN='\033[0;32m'
YLW='\033[1;33m'
BLU='\033[0;34m'
BLD='\033[1m'
RST='\033[0m'

info()    { echo -e "${BLU}::${RST} $*"; }
success() { echo -e "${GRN}✓${RST} $*"; }
warn()    { echo -e "${YLW}⚠${RST}  $*"; }
error()   { echo -e "${RED}✗${RST}  $*" >&2; }
die()     { error "$*"; exit 1; }

# ── Defaults ──────────────────────────────────────────────────────────────────
GLOBAL=false
ICON=""
APP_NAME=""
CATEGORY="Utility"
UPDATE_FILE=""
FORCE=false
UNINSTALL=false

# ── Help ──────────────────────────────────────────────────────────────────────
usage() {
cat <<EOF
${BLD}install-app${RST} — install executables with .desktop menu integration

${BLD}USAGE${RST}
  install-app [OPTIONS] <executable>

${BLD}OPTIONS${RST}
  -g, --global           Install system-wide (/usr/local/bin, /usr/share/)
                         Requires sudo / root
  -i, --icon <file>      Icon file (png/svg/xpm)
  -n, --name <name>      Display name (default: derived from filename)
  -c, --category <cat>   .desktop category (default: Utility)
  --update <new_file>    Replace binary with new version (keeps .desktop & icon)
  --uninstall            Remove binary, icon and .desktop entry
  --force                Overwrite/remove without confirmation
  -h, --help             Show this help

${BLD}EXAMPLES${RST}
  install-app ~/Downloads/MyApp.AppImage
  install-app -g -i ~/icon.png -n "My App" ~/Downloads/MyApp.AppImage
  install-app --update ~/Downloads/MyApp-v2.AppImage -n "My App"
  install-app --uninstall -n "My App"
  sudo install-app -g --uninstall -n "My App"
EOF
}

# ── Argument parsing ──────────────────────────────────────────────────────────
POSITIONAL=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    -g|--global)   GLOBAL=true; shift ;;
    -i|--icon)     ICON="$2"; shift 2 ;;
    -n|--name)     APP_NAME="$2"; shift 2 ;;
    -c|--category) CATEGORY="$2"; shift 2 ;;
    --update)      UPDATE_FILE="$2"; shift 2 ;;
    --uninstall)   UNINSTALL=true; shift ;;
    --force)       FORCE=true; shift ;;
    -h|--help)     usage; exit 0 ;;
    -*)            die "Unknown option: $1. Use -h for help." ;;
    *)             POSITIONAL+=("$1"); shift ;;
  esac
done

# ── Resolve executable ────────────────────────────────────────────────────────
if $UNINSTALL; then
  # uninstall: -n required OR positional arg for name derivation
  if [[ -z "$APP_NAME" ]]; then
    if [[ ${#POSITIONAL[@]} -gt 0 ]]; then
      SRC_FILE="$(realpath "${POSITIONAL[0]}" 2>/dev/null || echo "${POSITIONAL[0]}")"
    else
      die "Provide app name via -n <name> or pass the original executable as argument."
    fi
  else
    SRC_FILE=""
  fi
elif [[ -n "$UPDATE_FILE" ]]; then
  SRC_FILE="$UPDATE_FILE"
  SRC_FILE="$(realpath "$SRC_FILE")"
  [[ -f "$SRC_FILE" ]] || die "File not found: $SRC_FILE"
else
  [[ ${#POSITIONAL[@]} -eq 0 ]] && { usage; exit 1; }
  SRC_FILE="${POSITIONAL[0]}"
  SRC_FILE="$(realpath "$SRC_FILE")"
  [[ -f "$SRC_FILE" ]] || die "File not found: $SRC_FILE"
fi

# ── Derive app name ───────────────────────────────────────────────────────────
if [[ -z "$APP_NAME" ]]; then
  BASENAME="$(basename "${SRC_FILE:-}")"
  # Strip known extensions: .AppImage, .appimage, .sh, .py, .bin, .elf
  APP_NAME="${BASENAME%%.AppImage}"
  APP_NAME="${APP_NAME%%.appimage}"
  APP_NAME="${APP_NAME%%.sh}"
  APP_NAME="${APP_NAME%%.py}"
  APP_NAME="${APP_NAME%%.bin}"
  APP_NAME="${APP_NAME%%.elf}"
  # Strip version suffixes like -x86_64, -1.2.3, _amd64
  APP_NAME="$(echo "$APP_NAME" | sed -E 's/[-_][vV]?[0-9]+[._0-9]*[-_]?(x86_64|amd64|arm64|aarch64)?$//')"
  APP_NAME="$(echo "$APP_NAME" | sed -E 's/[-_](x86_64|amd64|arm64|aarch64)$//')"
fi

DISPLAY_NAME="$APP_NAME"
BIN_NAME="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')"
DESKTOP_NAME="$(echo "$BIN_NAME" | tr '[:upper:]' '[:lower:]')"

# ── Paths ─────────────────────────────────────────────────────────────────────
if $GLOBAL; then
  BIN_DIR="/usr/local/bin"
  ICON_DIR="/usr/share/icons/hicolor/256x256/apps"
  DESKTOP_DIR="/usr/share/applications"
  DESKTOP_DB_DIR="/usr/share/applications"
else
  BIN_DIR="$HOME/.local/bin"
  ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
  DESKTOP_DIR="$HOME/.local/share/applications"
  DESKTOP_DB_DIR="$HOME/.local/share/applications"
fi

DEST_BIN="$BIN_DIR/$BIN_NAME"
DESKTOP_FILE="$DESKTOP_DIR/${DESKTOP_NAME}.desktop"

# ── Sudo check ────────────────────────────────────────────────────────────────
if $GLOBAL && [[ $EUID -ne 0 ]]; then
  die "System-wide install requires root. Run: sudo install-app -g $*"
fi

# ── UNINSTALL MODE ────────────────────────────────────────────────────────────
if $UNINSTALL; then
  info "Uninstalling ${BLD}$DISPLAY_NAME${RST}"

  FOUND=false

  # Check both scopes
  USER_BIN="$HOME/.local/bin/$BIN_NAME"
  SYS_BIN="/usr/local/bin/$BIN_NAME"

  if $GLOBAL; then
    BINS_TO_CHECK=("$SYS_BIN")
    ICON_DIRS_TO_CHECK=("/usr/share/icons/hicolor")
    DESKTOP_FILES_TO_CHECK=("/usr/share/applications/${DESKTOP_NAME}.desktop")
    DB_DIR="/usr/share/applications"
  else
    BINS_TO_CHECK=("$USER_BIN")
    ICON_DIRS_TO_CHECK=("$HOME/.local/share/icons/hicolor")
    DESKTOP_FILES_TO_CHECK=("$HOME/.local/share/applications/${DESKTOP_NAME}.desktop")
    DB_DIR="$HOME/.local/share/applications"
  fi

  # Sudo check for global
  if $GLOBAL && [[ $EUID -ne 0 ]]; then
    die "System-wide uninstall requires root. Run: sudo install-app -g --uninstall -n \"$DISPLAY_NAME\""
  fi

  if ! $FORCE; then
    warn "About to remove:"
    for b in "${BINS_TO_CHECK[@]}"; do [[ -f "$b" ]] && echo "    $b"; done
    for d in "${DESKTOP_FILES_TO_CHECK[@]}"; do [[ -f "$d" ]] && echo "    $d"; done
    for idir in "${ICON_DIRS_TO_CHECK[@]}"; do
      find "$idir" -name "${DESKTOP_NAME}.*" 2>/dev/null | while read -r f; do echo "    $f"; done
    done
    read -rp "  Continue? [y/N] " yn
    [[ "${yn,,}" == "y" ]] || die "Aborted."
  fi

  # Remove binary
  for b in "${BINS_TO_CHECK[@]}"; do
    if [[ -f "$b" ]]; then
      rm -f "$b"
      success "Removed binary: $b"
      FOUND=true
    fi
  done

  # Remove .desktop
  for d in "${DESKTOP_FILES_TO_CHECK[@]}"; do
    if [[ -f "$d" ]]; then
      rm -f "$d"
      success "Removed .desktop: $d"
      FOUND=true
    fi
  done

  # Remove icon(s)
  for idir in "${ICON_DIRS_TO_CHECK[@]}"; do
    while IFS= read -r iconfile; do
      rm -f "$iconfile"
      success "Removed icon: $iconfile"
      FOUND=true
    done < <(find "$idir" -name "${DESKTOP_NAME}.*" 2>/dev/null)
  done

  # Update desktop db
  if command -v update-desktop-database &>/dev/null; then
    update-desktop-database "$DB_DIR" 2>/dev/null || true
  fi

  $FOUND || warn "Nothing found to remove for '$BIN_NAME'"
  exit 0
fi

# ── UPDATE MODE ───────────────────────────────────────────────────────────────
if [[ -n "$UPDATE_FILE" ]]; then
  info "Update mode for: ${BLD}$DISPLAY_NAME${RST}"

  # Find installed binary
  if [[ ! -f "$DEST_BIN" ]]; then
    # Try the other scope
    ALT_BIN=""
    if $GLOBAL; then
      ALT_BIN="$HOME/.local/bin/$BIN_NAME"
    else
      ALT_BIN="/usr/local/bin/$BIN_NAME"
    fi
    if [[ -f "$ALT_BIN" ]]; then
      DEST_BIN="$ALT_BIN"
      warn "Found binary in alternate location: $DEST_BIN"
    else
      die "No installed binary found for '$BIN_NAME'. Install it first."
    fi
  fi

  info "Replacing: $DEST_BIN"
  cp "$SRC_FILE" "$DEST_BIN"
  chmod +x "$DEST_BIN"
  success "Binary updated: $DEST_BIN"
  exit 0
fi

# ── INSTALL MODE ──────────────────────────────────────────────────────────────
info "Installing ${BLD}$DISPLAY_NAME${RST} $(if $GLOBAL; then echo '(system-wide)'; else echo "(user: $USER)"; fi)"

# Confirm overwrite
if [[ -f "$DEST_BIN" ]] && ! $FORCE; then
  warn "Binary already exists: $DEST_BIN"
  read -rp "  Overwrite? [y/N] " yn
  [[ "${yn,,}" == "y" ]] || die "Aborted."
fi

# Create directories
mkdir -p "$BIN_DIR" "$ICON_DIR" "$DESKTOP_DIR"

# ── Copy binary ───────────────────────────────────────────────────────────────
cp "$SRC_FILE" "$DEST_BIN"
chmod +x "$DEST_BIN"
success "Binary installed: $DEST_BIN"

# ── Handle icon ───────────────────────────────────────────────────────────────
ICON_VALUE="application-x-executable"

if [[ -n "$ICON" ]]; then
  ICON="$(realpath "$ICON")"
  [[ -f "$ICON" ]] || die "Icon file not found: $ICON"
  ICON_EXT="${ICON##*.}"
  DEST_ICON="$ICON_DIR/${DESKTOP_NAME}.${ICON_EXT}"
  cp "$ICON" "$DEST_ICON"
  ICON_VALUE="$DEST_ICON"
  success "Icon installed: $DEST_ICON"
else
  info "No icon provided — using default: application-x-executable"
fi

# ── Create .desktop file ──────────────────────────────────────────────────────
cat > "$DESKTOP_FILE" <<EOF
[Desktop Entry]
Type=Application
Name=$DISPLAY_NAME
Exec=$DEST_BIN %F
Icon=$ICON_VALUE
Categories=$CATEGORY;
Terminal=false
StartupNotify=true
EOF

success ".desktop created: $DESKTOP_FILE"

# ── Update desktop database ───────────────────────────────────────────────────
if command -v update-desktop-database &>/dev/null; then
  update-desktop-database "$DESKTOP_DB_DIR" 2>/dev/null && \
    success "Desktop database updated"
fi

# Update icon cache (non-critical)
if command -v gtk-update-icon-cache &>/dev/null; then
  ICON_THEME_DIR="$(dirname "$(dirname "$(dirname "$ICON_DIR")")")"
  gtk-update-icon-cache -q "$ICON_THEME_DIR" 2>/dev/null || true
fi

# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo -e "${BLD}── Summary ──────────────────────────────${RST}"
echo -e "  Name       : ${BLD}$DISPLAY_NAME${RST}"
echo -e "  Binary     : $DEST_BIN"
echo -e "  .desktop   : $DESKTOP_FILE"
echo -e "  Icon       : $ICON_VALUE"
echo -e "  Scope      : $(if $GLOBAL; then echo 'system-wide'; else echo "user ($USER)"; fi)"
echo ""
echo -e "  Run with   : ${BLD}$BIN_NAME${RST}"
echo -e "${BLD}─────────────────────────────────────────${RST}"
