#!/usr/bin/env bash
#
# claude-desktop-fedora.sh
#
# Instala o actualiza Claude Desktop en Fedora (GNOME) a partir del paquete
# .deb oficial para Debian/Ubuntu. Automatiza el proceso descrito en:
#   https://montfort.dev/notes/claude-desktop-in-fedora
#
# Qué hace:
#   1. Lee el índice APT (Packages) y elige la versión más reciente (o la que
#      pases con --version).
#   2. Si esa versión ya está instalada, no hace nada (salvo --force).
#   3. Descarga el .deb a un temporal y verifica su SHA256 contra el índice.
#   4. Lo extrae, BORRA la instalación previa en /opt/claude-desktop y coloca
#      la nueva (evita el bug de `mv` que anida la carpeta al actualizar).
#   5. Instala dependencias, crea el wrapper /usr/local/bin/claude-desktop y la
#      entrada .desktop, y refresca el menú de aplicaciones.
#
# Uso:
#   ./claude-desktop-fedora.sh                 # instala/actualiza a la última
#   ./claude-desktop-fedora.sh --version 1.18286.0
#   ./claude-desktop-fedora.sh --force         # reinstala aunque ya esté al día
#   ./claude-desktop-fedora.sh --dry-run       # muestra qué haría, sin tocar nada
#   ./claude-desktop-fedora.sh --no-deps       # omite la instalación de dependencias
#
# No lo ejecutes con sudo: el script pide sudo solo en los pasos que lo
# necesitan, para que la entrada .desktop quede en TU $HOME y no en el de root.

set -euo pipefail

# --- Configuración --------------------------------------------------------

APT_BASE="https://downloads.claude.ai/claude-desktop/apt/stable"
PACKAGES_URL="${APT_BASE}/dists/stable/main/binary-amd64/Packages"

INSTALL_DIR="/opt/claude-desktop"
WRAPPER="/usr/local/bin/claude-desktop"
BIN_REL="usr/lib/claude-desktop/claude-desktop"
ICON_REL="usr/share/icons/hicolor/256x256/apps/claude-desktop.png"
VERSION_FILE="${INSTALL_DIR}/VERSION"

# Equivalentes en Fedora de las dependencias del .deb.
DEPS=(gtk3 libnotify nss libdrm mesa-libgbm libxcb libsecret libXtst libuuid
      alsa-lib xdg-desktop-portal xdg-desktop-portal-gnome)

# --- Utilidades -----------------------------------------------------------

info()  { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn()  { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
err()   { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; }
die()   { err "$*"; exit 1; }

# Usuario/HOME reales, incluso si alguien corre el script con sudo.
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
  REAL_USER="${SUDO_USER}"
  REAL_HOME="$(getent passwd "${SUDO_USER}" | cut -d: -f6)"
else
  REAL_USER="${USER:-$(id -un)}"
  REAL_HOME="${HOME}"
fi

# Ejecuta un comando como el usuario real (para no crear archivos como root).
run_as_user() {
  if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
    sudo -u "${REAL_USER}" "$@"
  else
    "$@"
  fi
}

# --- Argumentos -----------------------------------------------------------

WANT_VERSION=""
FORCE=0
DRY_RUN=0
INSTALL_DEPS=1

usage() {
  sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
  exit "${1:-0}"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --version) WANT_VERSION="${2:-}"; shift 2 ;;
    --version=*) WANT_VERSION="${1#*=}"; shift ;;
    --force) FORCE=1; shift ;;
    --dry-run) DRY_RUN=1; shift ;;
    --no-deps) INSTALL_DEPS=0; shift ;;
    -h|--help) usage 0 ;;
    *) err "Opción desconocida: $1"; usage 1 ;;
  esac
done

# --- Comprobaciones previas ----------------------------------------------

[[ "$(uname -m)" == "x86_64" ]] || die "Este paquete es amd64; tu arquitectura es $(uname -m)."

for cmd in curl awk sort sha256sum; do
  command -v "$cmd" >/dev/null 2>&1 || die "Falta el comando '$cmd'."
done

# dpkg-deb no viene de fábrica en Fedora; lo arrastra el paquete 'alien'.
if ! command -v dpkg-deb >/dev/null 2>&1; then
  if [[ $DRY_RUN -eq 1 ]]; then
    warn "dpkg-deb no está instalado (lo instalaría con: sudo dnf install -y alien)."
  else
    info "dpkg-deb no está instalado; instalando 'alien' (arrastra dpkg)…"
    sudo dnf install -y alien
  fi
fi

# Cachea credenciales de sudo por adelantado (salvo dry-run).
if [[ $DRY_RUN -eq 0 && "${EUID}" -ne 0 ]]; then
  sudo -v || die "Se requieren privilegios de sudo."
fi

# --- Temporal + limpieza --------------------------------------------------

TMP="$(mktemp -d)"
cleanup() { rm -rf "${TMP}"; }
trap cleanup EXIT

# --- Resolver versión a instalar -----------------------------------------

info "Consultando índice APT…"
curl -fsSL -o "${TMP}/Packages" "${PACKAGES_URL}" \
  || die "No se pudo descargar el índice Packages."

# Extrae 'version|filename|sha256' de cada bloque del índice.
parse_packages() {
  awk 'BEGIN { RS=""; FS="\n" }
  {
    v=""; fn=""; sha=""
    for (i=1; i<=NF; i++) {
      if      ($i ~ /^Version: /)  v   = substr($i, 10)
      else if ($i ~ /^Filename: /) fn  = substr($i, 11)
      else if ($i ~ /^SHA256: /)   sha = substr($i, 9)
    }
    if (v != "" && fn != "") print v "|" fn "|" sha
  }' "${TMP}/Packages"
}

if [[ -n "${WANT_VERSION}" ]]; then
  ENTRY="$(parse_packages | awk -F'|' -v v="${WANT_VERSION}" '$1 == v { print; exit }')"
  [[ -n "${ENTRY}" ]] || die "La versión '${WANT_VERSION}' no está en el índice."
else
  # Orden de versión (sort -V) y toma la mayor.
  ENTRY="$(parse_packages | sort -t'|' -k1,1 -V | tail -n1)"
  [[ -n "${ENTRY}" ]] || die "No se encontró ninguna versión en el índice."
fi

TARGET_VERSION="${ENTRY%%|*}"
REST="${ENTRY#*|}"
FILENAME="${REST%%|*}"
SHA256="${REST##*|}"
DEB_URL="${APT_BASE}/${FILENAME}"

# --- ¿Ya está instalada? --------------------------------------------------

INSTALLED_VERSION=""
[[ -f "${VERSION_FILE}" ]] && INSTALLED_VERSION="$(cat "${VERSION_FILE}" 2>/dev/null || true)"

info "Instalada: ${INSTALLED_VERSION:-ninguna}  |  objetivo: ${TARGET_VERSION}"

if [[ "${INSTALLED_VERSION}" == "${TARGET_VERSION}" && $FORCE -eq 0 ]]; then
  info "Ya tienes la versión ${TARGET_VERSION}. Nada que hacer (usa --force para reinstalar)."
  exit 0
fi

if [[ $DRY_RUN -eq 1 ]]; then
  cat <<EOF

--- DRY RUN: esto es lo que haría ---
  Versión objetivo : ${TARGET_VERSION}
  Descargar        : ${DEB_URL}
  Verificar SHA256 : ${SHA256:-<no disponible>}
  Reemplazar       : ${INSTALL_DIR}  (rm -rf previo si existe)
  Wrapper          : ${WRAPPER}
  Deps (dnf)       : $([[ $INSTALL_DEPS -eq 1 ]] && echo "${DEPS[*]}" || echo "(omitidas)")
  .desktop         : ${REAL_HOME}/.local/share/applications/claude-desktop.desktop
--------------------------------------
EOF
  exit 0
fi

# --- Descargar y verificar ------------------------------------------------

info "Descargando ${FILENAME##*/}…"
curl -fSL --progress-bar -o "${TMP}/claude-desktop.deb" "${DEB_URL}" \
  || die "Falló la descarga del .deb."

if [[ -n "${SHA256}" ]]; then
  info "Verificando SHA256…"
  echo "${SHA256}  ${TMP}/claude-desktop.deb" | sha256sum -c - \
    || die "El SHA256 no coincide; descarga corrupta o manipulada. Aborto."
else
  warn "El índice no traía SHA256; omito la verificación de integridad."
fi

# --- Extraer --------------------------------------------------------------

info "Extrayendo paquete…"
dpkg-deb -x "${TMP}/claude-desktop.deb" "${TMP}/deb_extract"
[[ -x "${TMP}/deb_extract/${BIN_REL}" ]] \
  || die "No se encontró el binario esperado en ${BIN_REL}."

# --- Dependencias ---------------------------------------------------------

if [[ $INSTALL_DEPS -eq 1 ]]; then
  info "Instalando dependencias en Fedora (idempotente)…"
  sudo dnf install -y "${DEPS[@]}"
else
  info "Omitiendo instalación de dependencias (--no-deps)."
fi

# --- Colocar en /opt (reemplazando cualquier instalación previa) ----------

info "Instalando en ${INSTALL_DIR}…"
sudo rm -rf "${INSTALL_DIR}"                       # <-- clave: evita anidar al actualizar
sudo mv "${TMP}/deb_extract" "${INSTALL_DIR}"
echo "${TARGET_VERSION}" | sudo tee "${VERSION_FILE}" >/dev/null

# --- Wrapper --------------------------------------------------------------

info "Creando wrapper en ${WRAPPER}…"
sudo tee "${WRAPPER}" >/dev/null <<EOF
#!/bin/bash
${INSTALL_DIR}/${BIN_REL} "\$@"
EOF
sudo chmod +x "${WRAPPER}"

# --- Integración con GNOME (.desktop) ------------------------------------

DESKTOP_DIR="${REAL_HOME}/.local/share/applications"
DESKTOP_FILE="${DESKTOP_DIR}/claude-desktop.desktop"
ICON_PATH="${INSTALL_DIR}/${ICON_REL}"
[[ -f "${ICON_PATH}" ]] || ICON_PATH="claude-desktop"   # fallback al tema de iconos

info "Creando entrada de aplicación en ${DESKTOP_FILE}…"
run_as_user mkdir -p "${DESKTOP_DIR}"
run_as_user tee "${DESKTOP_FILE}" >/dev/null <<EOF
[Desktop Entry]
Name=Claude Desktop
Exec=${WRAPPER}
Icon=${ICON_PATH}
Type=Application
Categories=Utility;
EOF

command -v update-desktop-database >/dev/null 2>&1 \
  && run_as_user update-desktop-database "${DESKTOP_DIR}" || true

info "Listo. Claude Desktop ${TARGET_VERSION} instalado."
info "Ejecuta 'claude-desktop' o búscalo en el menú de aplicaciones."
