#!/usr/bin/env bash
set -euo pipefail

BASE_URL="${KROMA_BASE_URL:-https://kroma.tenkayro.de}"
INSTALL_ROOT="${HOME}/.local/opt/kroma"
APPIMAGE_DEST="${INSTALL_ROOT}/kroma.AppImage"
APPDIR_DEST="${INSTALL_ROOT}/appdir"
BIN_DIR="${HOME}/.local/bin"
LAUNCHER="${BIN_DIR}/kroma"
APPS_DIR="${HOME}/.local/share/applications"
ICON_DIR="${HOME}/.local/share/icons/hicolor/256x256/apps"
MIME_DIR="${HOME}/.local/share/mime/packages"

c_bold=""
c_dim=""
c_green=""
c_yellow=""
c_red=""
c_reset=""
if [ -t 1 ]; then
  c_bold="\033[1m"
  c_dim="\033[2m"
  c_green="\033[32m"
  c_yellow="\033[33m"
  c_red="\033[31m"
  c_reset="\033[0m"
fi

info() { printf "${c_bold}==>${c_reset} %s\n" "$*"; }
ok()   { printf "${c_green}✓${c_reset} %s\n" "$*"; }
warn() { printf "${c_yellow}!${c_reset} %s\n" "$*"; }
err()  { printf "${c_red}✗${c_reset} %s\n" "$*" >&2; }

ARCH="$(uname -m)"
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "amd64" ]; then
  err "kroma currently provides x86_64 builds (detected: $ARCH)."
  exit 1
fi

run_priv() {
  if [ "$(id -u)" -eq 0 ]; then
    "$@"
  elif command -v sudo >/dev/null 2>&1; then
    sudo "$@"
  elif command -v doas >/dev/null 2>&1; then
    doas "$@"
  else
    err "Root privileges needed to install missing system dependencies ($*)."
    return 1
  fi
}

install_deps() {
  info "Checking & installing Linux runtime dependencies..."

  if command -v apt-get >/dev/null 2>&1; then
    local pkgs=(
      curl ca-certificates libgl1 libgl1-mesa-dri libegl1 libx11-6 libxrandr2 libxinerama1 libxcursor1 libxi6
      zenity xdg-utils desktop-file-utils shared-mime-info libpulse0 dbus-x11
    )
    if apt-cache show libasound2t64 >/dev/null 2>&1; then
      pkgs+=(libasound2t64)
    else
      pkgs+=(libasound2)
    fi
    if apt-cache show libfuse2t64 >/dev/null 2>&1; then
      pkgs+=(libfuse2t64)
    elif apt-cache show libfuse2 >/dev/null 2>&1; then
      pkgs+=(libfuse2)
    fi
    local missing=()
    for p in "${pkgs[@]}"; do
      dpkg -s "$p" >/dev/null 2>&1 || missing+=("$p")
    done
    if [ "${#missing[@]}" -gt 0 ]; then
      info "Installing apt packages: ${missing[*]}"
      run_priv apt-get update -qq
      DEBIAN_FRONTEND=noninteractive run_priv apt-get install -y --no-install-recommends "${missing[@]}"
    fi
    ok "Dependencies ready (apt)"

  elif command -v pacman >/dev/null 2>&1; then
    local pkgs=(
      curl ca-certificates mesa libglvnd libx11 libxrandr libxinerama libxcursor libxi
      alsa-lib libpulse zenity xdg-utils desktop-file-utils shared-mime-info fuse2
    )
    local missing=()
    for p in "${pkgs[@]}"; do
      pacman -Qi "$p" >/dev/null 2>&1 || missing+=("$p")
    done
    if [ "${#missing[@]}" -gt 0 ]; then
      info "Installing pacman packages: ${missing[*]}"
      run_priv pacman -Sy --needed --noconfirm "${missing[@]}"
    fi
    ok "Dependencies ready (pacman)"

  elif command -v dnf >/dev/null 2>&1; then
    local pkgs=(
      curl ca-certificates mesa-libGL libX11 libXrandr libXinerama libXcursor libXi
      alsa-lib pulseaudio-libs zenity xdg-utils desktop-file-utils shared-mime-info fuse fuse-libs
    )
    info "Ensuring dnf packages..."
    run_priv dnf install -y -q "${pkgs[@]}"
    ok "Dependencies ready (dnf)"

  elif command -v zypper >/dev/null 2>&1; then
    local pkgs=(
      curl ca-certificates Mesa-libGL1 libX11-6 libXrandr2 libXinerama1 libXcursor1 libXi6
      libasound2 libpulse0 zenity xdg-utils desktop-file-utils shared-mime-info libfuse2
    )
    info "Ensuring zypper packages..."
    run_priv zypper --non-interactive install -y --no-recommends "${pkgs[@]}"
    ok "Dependencies ready (zypper)"

  elif command -v xbps-install >/dev/null 2>&1; then
    local pkgs=(
      curl ca-certificates MesaLib libX11 libXrandr libXinerama libXcursor libXi
      alsa-lib pulseaudio zenity xdg-utils desktop-file-utils shared-mime-info fuse
    )
    run_priv xbps-install -Sy "${pkgs[@]}"
    ok "Dependencies ready (xbps)"

  elif command -v apk >/dev/null 2>&1; then
    warn "Alpine (musl) detected — installing X11/Mesa/gcompat compatibility libraries."
    run_priv apk add --no-cache curl ca-certificates gcompat mesa-gl libx11 libxrandr libxinerama libxcursor libxi alsa-lib pulseaudio zenity xdg-utils desktop-file-utils shared-mime-info fuse

  elif command -v nix-env >/dev/null 2>&1; then
    warn "NixOS / Nix detected — skipping imperative package install. FUSE/appimage-run or auto-extract will be used."

  else
    warn "Unknown package manager — ensure OpenGL, X11 (libX11/libXrandr/libXi/libXcursor/libXinerama), ALSA/PulseAudio, zenity and xdg-utils are installed."
  fi
}

parse_json_field() {
  local json="$1"
  local field="$2"
  if command -v python3 >/dev/null 2>&1; then
    python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get(sys.argv[1], ""))' "$field" <<<"$json"
  else
    printf '%s' "$json" | tr -d '\r\n' | sed -n "s/.*\"${field}\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p"
  fi
}

install_deps

if ! command -v curl >/dev/null 2>&1; then
  err "curl is required to download kroma."
  exit 1
fi

info "Fetching latest Linux release metadata from ${BASE_URL}..."
META_JSON="$(curl -fsSL "${BASE_URL}/download/linux/latest.json")"
VERSION="$(parse_json_field "$META_JSON" "version")"
EXPECTED_SHA="$(parse_json_field "$META_JSON" "sha256")"

if [ -z "$VERSION" ]; then
  err "No Linux AppImage release is published on ${BASE_URL} yet."
  exit 1
fi

mkdir -p "$INSTALL_ROOT" "$BIN_DIR" "$APPS_DIR" "$ICON_DIR" "$MIME_DIR"
TMP_DL="${APPIMAGE_DEST}.download"
trap 'rm -f "$TMP_DL"' EXIT

info "Downloading kroma v${VERSION} (.AppImage)..."
curl -#fL -o "$TMP_DL" "${BASE_URL}/download/linux"

if [ -n "$EXPECTED_SHA" ] && command -v sha256sum >/dev/null 2>&1; then
  ACTUAL_SHA="$(sha256sum "$TMP_DL" | awk '{print $1}')"
  if [ "${ACTUAL_SHA,,}" != "${EXPECTED_SHA,,}" ]; then
    err "SHA-256 verification failed! Expected ${EXPECTED_SHA}, got ${ACTUAL_SHA}"
    exit 1
  fi
  ok "SHA-256 verified (${ACTUAL_SHA:0:12}...)"
fi

chmod 755 "$TMP_DL"
mv -f "$TMP_DL" "$APPIMAGE_DEST"

USE_EXTRACTED=0
if [ -e /dev/fuse ] && "$APPIMAGE_DEST" --appimage-help >/dev/null 2>&1; then
  # Test whether FUSE actually mounts on this host; fallback cleanly to extracted AppDir if blocked (containers/Ubuntu no-fuse2)
  if ! APPIMAGE_SILENT_INSTALL=1 timeout 4 "$APPIMAGE_DEST" --appimage-offset >/dev/null 2>&1; then
    USE_EXTRACTED=1
  fi
  if command -v ldconfig >/dev/null 2>&1 && ! ldconfig -p 2>/dev/null | grep -q "libfuse.so.2"; then
    USE_EXTRACTED=1
  fi
else
  USE_EXTRACTED=1
fi

if [ "$USE_EXTRACTED" -eq 1 ]; then
  info "FUSE2 unavailable — extracting AppImage locally for instant zero-FUSE startup..."
  rm -rf "$APPDIR_DEST"
  TMP_EX="$(mktemp -d)"
  (
    cd "$TMP_EX"
    "$APPIMAGE_DEST" --appimage-extract >/dev/null
    mv "$TMP_EX/squashfs-root" "$APPDIR_DEST"
  )
  rm -rf "$TMP_EX"

  cat > "$LAUNCHER" <<EOF
#!/usr/bin/env bash
if [ "\${KROMA_HW_GL:-0}" != "1" ] && grep -qi "hypervisor" /proc/cpuinfo 2>/dev/null; then
  export LIBGL_ALWAYS_SOFTWARE=1
  export GALLIUM_DRIVER=llvmpipe
  if [ -z "\${LP_NUM_THREADS:-}" ]; then
    CORES="\$(nproc 2>/dev/null || echo 2)"
    [ "\$CORES" -gt 2 ] && export LP_NUM_THREADS=\$(( CORES - 1 )) || export LP_NUM_THREADS=1
  fi
fi
export APPIMAGE="${APPIMAGE_DEST}"
exec "${APPDIR_DEST}/AppRun" "\$@"
EOF
  ok "Installed kroma v${VERSION} (auto-extracted mode)"
else
  rm -rf "$APPDIR_DEST"
  cat > "$LAUNCHER" <<EOF
#!/usr/bin/env bash
if [ "\${KROMA_HW_GL:-0}" != "1" ] && grep -qi "hypervisor" /proc/cpuinfo 2>/dev/null; then
  export LIBGL_ALWAYS_SOFTWARE=1
  export GALLIUM_DRIVER=llvmpipe
  if [ -z "\${LP_NUM_THREADS:-}" ]; then
    CORES="\$(nproc 2>/dev/null || echo 2)"
    [ "\$CORES" -gt 2 ] && export LP_NUM_THREADS=\$(( CORES - 1 )) || export LP_NUM_THREADS=1
  fi
fi
exec "${APPIMAGE_DEST}" "\$@"
EOF
  ok "Installed kroma v${VERSION} (AppImage mode)"
fi
chmod 755 "$LAUNCHER"

if [ -f "${APPDIR_DEST}/kroma.png" ]; then
  cp -f "${APPDIR_DEST}/kroma.png" "${ICON_DIR}/kroma.png"
else
  curl -fsSL -o "${ICON_DIR}/kroma.png" "${BASE_URL}/assets/kroma_logo.png" || true
fi

cat > "${MIME_DIR}/kroma.xml" <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
  <mime-type type="application/x-kroma-package">
    <comment>kroma Beatmap Package</comment>
    <glob pattern="*.kroma"/>
  </mime-type>
</mime-info>
EOF

cat > "${APPS_DIR}/kroma.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=kroma
Comment=4-Key Mania Rhythm Game
Exec="${LAUNCHER}" %u
Icon=kroma
Terminal=false
Categories=Game;Music;
MimeType=x-scheme-handler/kroma;application/x-kroma-package;
StartupWMClass=kroma
EOF

command -v update-mime-database >/dev/null 2>&1 && update-mime-database "${HOME}/.local/share/mime" >/dev/null 2>&1 || true
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "${APPS_DIR}" >/dev/null 2>&1 || true
command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -t -f "${HOME}/.local/share/icons/hicolor" >/dev/null 2>&1 || true
if command -v xdg-mime >/dev/null 2>&1; then
  xdg-mime default kroma.desktop x-scheme-handler/kroma >/dev/null 2>&1 || true
  xdg-mime default kroma.desktop application/x-kroma-package >/dev/null 2>&1 || true
fi

if [[ ":${PATH}:" != *":${BIN_DIR}:"* ]]; then
  for rc in "${HOME}/.bashrc" "${HOME}/.zshrc" "${HOME}/.profile"; do
    if [ -f "$rc" ] && ! grep -q '\.local/bin' "$rc" 2>/dev/null; then
      printf '\nexport PATH="$HOME/.local/bin:$PATH"\n' >> "$rc"
    fi
  done
  warn "Added ${BIN_DIR} to PATH (open a new shell or run: export PATH=\"\$HOME/.local/bin:\$PATH\")"
fi

printf "\n${c_green}${c_bold}kroma v%s installed!${c_reset}\n" "$VERSION"
printf "Run ${c_bold}kroma${c_reset} or launch it from your application menu.\n"
printf "${c_dim}AppImage: %s${c_reset}\n" "$APPIMAGE_DEST"
