#!/usr/bin/env bash
#
# A script to bootstrap dotfiles with a mapping of repo-file -> home-file:
# - Clones the repo if needed (when run outside ~/.dotfiles).
# - Installs packages (different for macOS vs. Ubuntu/Debian).
# - Changes shell to Fish if it's not already.
# - Symlinks dotfiles (with custom filename mapping), prompting to replace existing ones.

set -euo pipefail

################################################################################
# VARIABLES
################################################################################

DOTFILES_REPO="https://git.straybits.ca/jeff/dotfiles.git"
DOTFILES_DIR="$HOME/.dotfiles"

# Packages for macOS:
PACKAGES_MAC=(
  fish
  fzf
  bat
  ack
  fd
  age
  macvim
  mc
  lazygit
)

# Packages for Linux (assuming Ubuntu/Debian):
PACKAGES_LINUX=(
  git
  fish
  curl
  fzf
  bat
  ack
  fd-find
  age
  vim-tiny
  mc
)

#------------------------------------------------------------------------------
# Symlinking Dotfiles With Different Filenames
#
# In this associative array:
#   - The key is the file/directory name as it appears in the dotfiles repo.
#   - The value is the target path you want symlinked in your home directory.
#------------------------------------------------------------------------------
SYMLINKS=(
  "vim/config.vim:$HOME/.vim_runtime/my_configs.vim"
  "vim/plugins:$HOME/.vim_runtime/my_plugins"
  "fish:$HOME/.config/fish"
  "starship.toml:$HOME/.config/starship.toml"
  "mise-global.toml:$HOME/.config/mise/config.toml"
  "sway/config:$HOME/.config/sway/config"
  "sway/config.d:$HOME/.config/sway/config.d"
  "sway/wallpaper.jpg:$HOME/.config/sway/wallpaper.jpg"
  "sway/lock.jpg:$HOME/.config/sway/lock.jpg"
  "waybar/config.jsonc:$HOME/.config/waybar/config.jsonc"
  "waybar/style.css:$HOME/.config/waybar/style.css"
  "gitconfig:$HOME/.gitconfig"
  "gpg/gpg.conf:$HOME/.gnupg/gpg.conf"
)

TARGET_SHELL="fish"

################################################################################
# FUNCTIONS
################################################################################

detect_os() {
  local os
  os="$(uname -s)"
  case "${os}" in
      Linux*)   echo "linux" ;;
      Darwin*)  echo "mac"   ;;
      *)        echo "unsupported" ;;
  esac
}

install_package_linux() {
  local pkg="$1"
  # Check if installed:
  if ! dpkg -s "$pkg" >/dev/null 2>&1; then
    echo "Installing ${pkg} via apt..."
    sudo apt-get update -y
    sudo apt-get install -y "${pkg}"
  else
    echo "[OK] ${pkg} is already installed (Linux)."
  fi
}

install_package_mac() {
  local pkg="$1"
  # Check if installed:
  if ! brew ls --versions "$pkg" >/dev/null; then
    echo "Installing ${pkg} via Homebrew..."
    brew install "${pkg}"
  else
    echo "[OK] ${pkg} is already installed (macOS)."
  fi
}

install_packages() {
  local os="$1"

  if [ "${os}" = "mac" ]; then
    # Make sure Homebrew is installed first
    if ! command -v brew >/dev/null 2>&1; then
      echo "Homebrew not found, installing..."
      /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
      eval "$(/opt/homebrew/bin/brew shellenv)"
    fi

    # Install each macOS package if missing
    for pkg in "${PACKAGES_MAC[@]}"; do
      install_package_mac "${pkg}"
    done

  elif [ "${os}" = "linux" ]; then
    if command -v apt-get >/dev/null 2>&1; then
      echo "Warning: This script currently only supports apt-based distros on Linux."

      # Install each Linux package if missing
      for pkg in "${PACKAGES_LINUX[@]}"; do
        install_package_linux "${pkg}"
      done

      # No packages for lazygit on Linux :(
      install_lazygit_linux
    fi
  fi
}

install_lazygit_linux() {
    LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | \grep -Po '"tag_name": *"v\K[^"]*')
    arch="$(uname -m)"
    case "$arch" in
      x86_64)
        LAZYGIT_ARCH="Linux_x86_64"
        ;;
      aarch64)
        LAZYGIT_ARCH="Linux_arm64"
        ;;
      *)
        echo "Unsupported architecture: $arch"
        exit 1
        ;;
    esac
    echo "Downloading LazyGit v${LAZYGIT_VERSION} for ${LAZYGIT_ARCH}..."
    curl -Lo lazygit.tar.gz \
      "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_${LAZYGIT_ARCH}.tar.gz"
    tar xf lazygit.tar.gz lazygit
    sudo install lazygit -D -t /usr/local/bin/
    rm -f lazygit.tar.gz lazygit
}

install_awesome_vim() {
  if [ ! -d "$HOME/.vim_runtime" ]; then
    echo "Cloning Amix's vimrc..."
    git clone --depth=1 https://github.com/amix/vimrc.git "$HOME/.vim_runtime"
    echo "Installing Awesome Vimrc..."
    sh "$HOME/.vim_runtime/install_awesome_vimrc.sh"
  else
    echo "~/.vim_runtime already exists, skipping clone."
  fi
}

install_starship() {
  if ! command -v starship >/dev/null 2>&1; then
    echo "Installing Starship"
    curl -sS https://starship.rs/install.sh | sh
  else
    echo "Starship already installed"
  fi
}

install_mise() {
  if ! command -v mise >/dev/null 2>&1; then
    echo "Installing Mise"
    curl https://mise.run | sh
  else
    echo "Mise already installed"
  fi
}

change_shell_to_fish() {
  local fish_path
  fish_path="$(command -v fish)"
  if ! grep -Fxq "${fish_path}" /etc/shells; then
    echo "Adding ${fish_path} to /etc/shells..."
    # Some systems might require sudo for writing to /etc/shells
    echo "${fish_path}" | sudo tee -a /etc/shells
  else
    echo "Fish ${fish_path} already in /etc/shells..."
  fi

  if [[ $SHELL != *"${TARGET_SHELL}"* ]]; then
    # Check if fish is actually installed
    echo "Changing default shell to '${TARGET_SHELL}'..."
    sudo chsh -s "$(command -v fish)" "${USER}"
  else
    echo "[OK] Shell is already '${TARGET_SHELL}'."
  fi
}

create_symlinks() {
  # Iterate over the SYMLINKS associative array
  for pair in "${SYMLINKS[@]}"; do
    local src="${DOTFILES_DIR}/${pair%%:*}"
    local dest="${pair#*:}"

    # Ensure the parent directory of the destination exists
    mkdir -p "$(dirname "${dest}")"

    # Early exit if the link already exists
    if [ -e "${dest}" ] && [ -L "${dest}" ]; then
      echo "  -> Link '${dest}' to '${src}' exists."
      continue
    fi

    if [ -e "${dest}" ]; then
      # A real file or directory is in the way
      read -rp "File '${dest}' already exists. Overwrite? [y/N] " answer
      if [[ "${answer}" =~ ^[Yy]$ ]]; then
        rm -rf "${dest}"
        ln -s "${src}" "${dest}"
        echo "  -> Overwrote '${dest}' with symlink to '${src}'."
      else
        echo "  -> Skipped '${dest}'."
      fi
    else
      ln -s "${src}" "${dest}"
      echo "  -> Linked '${dest}' to '${src}'."
    fi
  done
}

deploy_dotfiles() {
  local os
  os="$(detect_os)"

  if [ "${os}" = "unsupported" ]; then
    echo "Unsupported OS: $(uname -s). Exiting."
    exit 1
  fi

  echo "1. Installing necessary packages..."
  install_packages "${os}"

  echo "2. Intalling awesome vim..."
  install_awesome_vim

  echo "3. Intalling Starship..."
  install_starship

  echo "4. Changing shell to '${TARGET_SHELL}' (if needed)..."
  change_shell_to_fish

  echo "5. Installing Mise"
  install_mise

  echo "6. Creating symlinks..."
  create_symlinks

  echo "Done!"
}

################################################################################
# MAIN SCRIPT
################################################################################

# If the script is not being run from ~/.dotfiles, we assume the user just curl'ed it.
if [ "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" != "${DOTFILES_DIR}" ]; then
  echo "Script is not in ${DOTFILES_DIR}; assuming first-time setup..."

  if [ ! -d "${DOTFILES_DIR}" ]; then
    echo "Cloning dotfiles repo into ${DOTFILES_DIR}..."
    git clone "${DOTFILES_REPO}" "${DOTFILES_DIR}"
  else
    echo "${DOTFILES_DIR} already exists. Pulling latest changes..."
    git -C "${DOTFILES_DIR}" pull --rebase
  fi

  echo "Running deployment from cloned repo..."
  cd "${DOTFILES_DIR}"
  bash install.sh  # re-invoke the script from within ~/.dotfiles
  exit 0
else
  echo "Script running from ${DOTFILES_DIR} - proceeding with deployment..."
  deploy_dotfiles
fi
