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

ASSUME_YES=0
REPLACE=0
for arg in "$@"; do
  case "$arg" in
    -y|--yes) ASSUME_YES=1 ;;
    --replace) REPLACE=1 ;;
    -h|--help)
      cat <<'USAGE'
tmux-starter: install tmux, git, and bun if missing, then write a small
beginner-friendly tmux config with TPM, tmux-sensible, and tmux-palette,
and install the plugins.

Usage: tmux-starter.sh [-y|--yes] [--replace]

  -y, --yes   Install prerequisites without asking first.
  --replace   Overwrite an existing ~/.tmux.conf (backed up to .bak).
              Without this, an existing config is left alone and the
              starter config is written alongside it.
USAGE
      exit 0
      ;;
    *) printf 'tmux-starter: unknown option: %s\n' "$arg" >&2; exit 1 ;;
  esac
done

say() {
  printf 'tmux-starter: %s\n' "$*"
}

die() {
  say "$*" >&2
  exit 1
}

has() {
  command -v "$1" >/dev/null 2>&1
}

# bun installs here and the shell rc that adds it to PATH is not sourced until
# the next login, so make it findable for the rest of this run.
export PATH="$HOME/.bun/bin:$PATH"

SUDO=""
if [ "$(id -u)" -ne 0 ]; then
  SUDO="sudo"
fi

detect_pkg_mgr() {
  if has brew; then echo brew
  elif has apt-get; then echo apt
  elif has dnf; then echo dnf
  elif has pacman; then echo pacman
  else echo none
  fi
}

PKG_MGR="$(detect_pkg_mgr)"

# tmux-palette drives display-popup with -b/-B/-s/-S, which arrived in tmux 3.3.
# Everything else in this config works on older tmux, so below 3.3 we drop the
# palette rather than refuse to run.
PALETTE_MIN_MAJOR=3
PALETTE_MIN_MINOR=3

tmux_supports_palette() {
  local v major minor
  has tmux || return 1
  v="$(tmux -V 2>/dev/null | awk '{print $2}')"
  v="${v#next-}"
  major="${v%%.*}"
  minor="${v#*.}"
  major="${major%%[!0-9]*}"
  minor="${minor%%[!0-9]*}"
  [ -n "$major" ] || return 1
  [ -n "$minor" ] || minor=0
  [ "$major" -gt "$PALETTE_MIN_MAJOR" ] && return 0
  [ "$major" -eq "$PALETTE_MIN_MAJOR" ] && [ "$minor" -ge "$PALETTE_MIN_MINOR" ] && return 0
  return 1
}

missing=()
for tool in git tmux; do
  has "$tool" || missing+=("$tool")
done
BUN_WAS_MISSING=0
if ! has bun; then
  missing+=(bun)
  BUN_WAS_MISSING=1
fi

# Commands are collected before anything runs so the user sees the whole plan
# in one prompt rather than being asked again partway through.
plan=()

pkgs=()
if [ ${#missing[@]} -gt 0 ]; then
  for tool in "${missing[@]}"; do
    [ "$tool" = "bun" ] || pkgs+=("$tool")
  done
fi

if [ ${#pkgs[@]} -gt 0 ]; then
  case "$PKG_MGR" in
    brew)   plan+=("brew install ${pkgs[*]}") ;;
    apt)    plan+=("$SUDO apt-get update" "$SUDO apt-get install -y ${pkgs[*]}") ;;
    dnf)    plan+=("$SUDO dnf install -y ${pkgs[*]}") ;;
    pacman) plan+=("$SUDO pacman -S --needed --noconfirm ${pkgs[*]}") ;;
    none)
      if [ "$(uname -s)" = "Darwin" ]; then
        die "Homebrew is not installed. Install it from https://brew.sh, then re-run this script."
      fi
      die "no supported package manager found. Install these yourself, then re-run: ${pkgs[*]}"
      ;;
  esac
fi

# An existing tmux too old for the palette is worth upgrading only where that is
# a one-liner. On apt/dnf/pacman reaching 3.3 means backports or a source build,
# so those systems get the palette-free config instead.
UPGRADING_TMUX=0
if has tmux && ! tmux_supports_palette && [ "$PKG_MGR" = "brew" ]; then
  plan+=("brew upgrade tmux")
  UPGRADING_TMUX=1
fi

if ! has bun; then
  if [ "$PKG_MGR" = "brew" ]; then
    plan+=("brew install oven-sh/bun/bun")
  else
    plan+=("curl -fsSL https://bun.sh/install | bash")
  fi
fi

if [ ${#plan[@]} -gt 0 ]; then
  if [ -n "$SUDO" ] && ! has sudo; then
    die "need sudo to install system packages, but sudo is not available. Re-run as root."
  fi

  if [ ${#missing[@]} -gt 0 ]; then
    missing_list="$(printf '%s, ' "${missing[@]}")"
    say "missing: ${missing_list%, }"
  fi
  if [ "$UPGRADING_TMUX" -eq 1 ]; then
    say "tmux $(tmux -V | awk '{print $2}') is too old for the command palette (needs 3.3+)"
  fi

  printf '\nAbout to run:\n'
  for cmd in "${plan[@]}"; do
    printf '  %s\n' "$cmd"
  done
  printf '\n'

  if [ "$ASSUME_YES" -eq 0 ]; then
    # stdin is the curl pipe when this script is run as `curl ... | bash`, so a
    # bare read would consume the script itself. Ask the terminal directly.
    # /dev/tty can exist and still fail to open when there is no controlling
    # terminal, so probe it by opening rather than by testing permissions.
    if ! (exec </dev/tty) 2>/dev/null; then
      say "no terminal available to confirm. Run the commands above yourself, or re-run with --yes." >&2
      exit 1
    fi
    printf 'Proceed? [y/N] '
    read -r reply </dev/tty
    case "$reply" in
      [yY]|[yY][eE][sS]) ;;
      *) die "cancelled" ;;
    esac
  fi

  for cmd in "${plan[@]}"; do
    say "running: $cmd"
    bash -c "$cmd"
  done

  export PATH="$HOME/.bun/bin:$PATH"

  if [ ${#missing[@]} -gt 0 ]; then
    for tool in "${missing[@]}"; do
      has "$tool" || die "$tool still not found after install. Install it manually, then re-run."
    done
  fi
  say "prerequisites installed"
fi

WANT_PALETTE=yes
if ! tmux_supports_palette; then
  WANT_PALETTE=no
fi

CONFIG_MARKER="# Written by tmux-starter. Safe to edit; re-running replaces this file."
MAIN_CONF="$HOME/.tmux.conf"
SIDE_CONF="$HOME/.config/tmux/starter.conf"

# An existing config that this script did not write belongs to someone with a
# tmux setup already. Overwriting it is the one genuinely destructive thing this
# script could do, so it writes alongside instead and says how to try it.
MODE=fresh
if [ -f "$MAIN_CONF" ]; then
  if grep -qF "$CONFIG_MARKER" "$MAIN_CONF" 2>/dev/null || [ "$REPLACE" -eq 1 ]; then
    MODE=replace
  else
    MODE=side
  fi
fi

if [ "$MODE" = "side" ]; then
  TMUX_CONF="$SIDE_CONF"
  PLUGIN_PATH="$HOME/.tmux-starter/plugins"
else
  TMUX_CONF="$MAIN_CONF"
  PLUGIN_PATH="$HOME/.tmux/plugins"
fi
TPM_DIR="$PLUGIN_PATH/tpm"

mkdir -p "$(dirname "$TMUX_CONF")" "$PLUGIN_PATH"

if [ ! -d "$TPM_DIR/.git" ]; then
  say "installing TPM into $TPM_DIR"
  git clone -q https://github.com/tmux-plugins/tpm "$TPM_DIR"
else
  say "TPM already exists at $TPM_DIR"
fi

if [ "$MODE" = "replace" ] && [ -f "$TMUX_CONF" ]; then
  cp "$TMUX_CONF" "$TMUX_CONF.bak"
  say "backed up existing $(basename "$TMUX_CONF") to $(basename "$TMUX_CONF").bak"
fi

{
  printf '%s\n\n' "$CONFIG_MARKER"
  cat <<'CONF_HEAD'
# Use Ctrl-a as the tmux prefix.
# Most tmux shortcuts begin by pressing Ctrl-a, releasing it, then pressing
# another key.
set -g prefix C-a
bind C-a send-prefix

# Make mouse selection, pane resizing, and scrollback easier while learning.
set -g mouse on

# Number windows and panes from 1 instead of 0.
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
CONF_HEAD

  if [ "$MODE" = "side" ]; then
    cat <<CONF_ISOLATE

# Keep these plugins out of the setup you already have. Must come before the
# plugin list and the tpm run line below.
set-environment -g TMUX_PLUGIN_MANAGER_PATH '$PLUGIN_PATH/'
CONF_ISOLATE
  fi

  cat <<'CONF_PLUGINS'

# TPM plugins.
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
CONF_PLUGINS

  if [ "$WANT_PALETTE" = "yes" ]; then
    cat <<'CONF_PALETTE'
set -g @plugin 'eduwass/tmux-palette'

# tmux-palette opens without the tmux prefix.
set -g @palette-key 'C-Space'
CONF_PALETTE
  fi

  printf "\n# Keep TPM at the very bottom.\nrun '%s/tpm/tpm'\n" "$PLUGIN_PATH"
} >"$TMUX_CONF"

say "wrote $TMUX_CONF"

# Clone the plugins directly rather than driving TPM's installer. TPM's helpers
# talk to a tmux server, and starting a server runs the user's config -- which
# for anyone using tmux-continuum means their whole saved session set gets
# restored into it. Cloning touches no server at all. TPM still sources these
# normally on the next tmux start; it just finds them already present.
clone_plugin() {
  local url="$1" dest="$PLUGIN_PATH/$2"
  if [ -d "$dest/.git" ]; then
    say "$2 already installed"
  else
    say "installing $2"
    git clone -q --depth 1 "$url" "$dest"
  fi
}

clone_plugin https://github.com/tmux-plugins/tmux-sensible tmux-sensible
if [ "$WANT_PALETTE" = "yes" ]; then
  clone_plugin https://github.com/eduwass/tmux-palette tmux-palette
  # The plugin does this itself on first load; doing it here means a failure
  # shows up now instead of as a message inside tmux later.
  if [ ! -d "$PLUGIN_PATH/tmux-palette/node_modules" ]; then
    say "fetching tmux-palette dependencies"
    (cd "$PLUGIN_PATH/tmux-palette" && bun install --silent) >/dev/null 2>&1 \
      || say "warning: 'bun install' failed; the palette will retry on first use"
  fi
fi

# The palette shows a shortcut next to a command only when it is told what that
# shortcut is -- it cannot read tmux's keymap. Writing the map turns the palette
# into a way to learn the keys instead of a way to avoid them.
if [ "$WANT_PALETTE" = "yes" ]; then
  PALETTE_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/tmux-palette"
  SHORTCUTS_JSON="$PALETTE_CONFIG_DIR/shortcuts.json"
  if [ -e "$SHORTCUTS_JSON" ]; then
    say "leaving your existing shortcuts.json alone"
  else
    mkdir -p "$PALETTE_CONFIG_DIR"
    cat >"$SHORTCUTS_JSON" <<'SHORTCUTS'
{
  "Split Horizontal": "C-a %",
  "Split Vertical": "C-a \"",
  "Close Pane": "C-a x",
  "Next Pane": "C-a o",
  "Display Pane Numbers": "C-a q",
  "Cycle Pane Layout": "C-a Space",
  "Swap Pane Up": "C-a {",
  "Swap Pane Down": "C-a }",
  "Zoom / Unzoom": "C-a z",
  "Enter Copy Mode": "C-a [",
  "Break to New Window": "C-a !",
  "New Window": "C-a c",
  "Next Window": "C-a n",
  "Previous Window": "C-a p",
  "Last Window": "C-a l",
  "Rename Window": "C-a ,",
  "Close Window": "C-a &",
  "Choose Session": "C-a s",
  "Detach": "C-a d",
  "Reload Config": "C-a R"
}
SHORTCUTS
    say "wrote $SHORTCUTS_JSON so the palette shows each command's shortcut"
  fi
fi

if [ "$WANT_PALETTE" = "no" ]; then
  say "tmux $(tmux -V | awk '{print $2}') is older than 3.3, so the command palette was left out"
  say "everything else is set up; upgrade tmux and re-run to add the palette"
fi

printf '\n'
if [ "$MODE" = "side" ]; then
  say "you already have a ~/.tmux.conf, so it was left untouched"
  printf '\nTry the starter setup without disturbing yours:\n\n'
  printf "  tmux -L starter -f %s new-session\n\n" "$TMUX_CONF"
  printf 'Done with it:      tmux -L starter kill-server\n'
  printf 'Want to keep it:   re-run this script with --replace\n'
else
  if [ -n "${TMUX:-}" ]; then
    tmux source-file "$TMUX_CONF"
    say "reloaded $(basename "$TMUX_CONF") in the current tmux session"
  else
    say "start tmux with: tmux"
  fi
  if [ "$WANT_PALETTE" = "yes" ]; then
    say "press Ctrl-Space to open the command palette"
  fi
fi

# bun's installer only edits shell rc files, so the shell this ran from still
# has no ~/.bun/bin on PATH. tmux-palette checks for bun when tmux starts and
# silently skips its key binding if it is missing -- which looks like the
# palette just not working.
if [ "$BUN_WAS_MISSING" -eq 1 ] && [ "$WANT_PALETTE" = "yes" ]; then
  printf '\n'
  say "bun was installed to ~/.bun/bin"
  say "open a new terminal before starting tmux, or the palette will not bind"
fi
