#!/bin/bash
#
# Purpose : Install and manage CoreDesktop sibling projects
#
# A sibling is a git repository cloned into ~/.local/share/siblings/<name>.
# If it has an install.sh, that runs once when the sibling is added.
# Every `update` pulls each sibling and runs its update.sh, if it has one
# (see updates.d/71-siblings.sh).

SIBLINGS="$HOME/.local/share/siblings"

usage() {
  cat << EOF
Usage: sibling [command]

  add [url]      Install a sibling project from a git repository (default)
  list           Show installed sibling projects
  remove <name>  Uninstall a sibling project
EOF
}

# Derive a directory name from a git URL, e.g.
# https://host/user/my-project.git -> my-project
sibling_name() {
  local name="${1%/}"
  name="${name##*/}"
  name="${name##*:}"
  echo "${name%.git}"
}

add_sibling() {
  local url="$1" name dir

  if [ -z "$url" ]; then
    read -rp "Git repository URL for the sibling project: " url
  fi
  if [ -z "$url" ]; then
    echo "No URL given."
    return 1
  fi

  name="$(sibling_name "$url")"
  if [[ -z "$name" || "$name" == .* ]]; then
    echo "Could not work out a project name from: $url"
    return 1
  fi
  dir="$SIBLINGS/$name"

  mkdir -p "$SIBLINGS"
  if [ -d "$dir/.git" ]; then
    local existing
    existing="$(git -C "$dir" remote get-url origin 2>/dev/null)"
    if [ "$existing" != "$url" ]; then
      echo "A different sibling named '$name' is already installed from: $existing"
      return 1
    fi
    echo "$name is already installed; pulling the latest version..."
    git -C "$dir" pull --ff-only || return 1
  else
    echo "Cloning $name..."
    git clone "$url" "$dir" || return 1
  fi

  if [ -f "$dir/install.sh" ]; then
    echo "Running $name's install.sh..."
    if ! (cd "$dir" && bash install.sh); then
      echo "$name's install.sh failed. Fix the problem and run 'sibling add $url' again."
      return 1
    fi
  fi

  # Confirm the install
  if [ -d "$dir/.git" ] && git -C "$dir" rev-parse --verify --quiet HEAD > /dev/null; then
    echo "Installed $name at $dir ($(git -C "$dir" log -1 --format='%h %s'))."
    echo "'update' will keep it up to date."
  else
    echo "$name does not look installed correctly at $dir."
    return 1
  fi
}

list_siblings() {
  local dir found=0
  for dir in "$SIBLINGS"/*/; do
    [ -d "$dir/.git" ] || continue
    found=1
    printf '%-24s %s  (%s)\n' "$(basename "$dir")" \
      "$(git -C "$dir" remote get-url origin 2>/dev/null)" \
      "$(git -C "$dir" log -1 --format='%h, %cr' 2>/dev/null)"
  done
  [ $found -eq 1 ] || echo "No sibling projects installed."
}

remove_sibling() {
  local name="$1"
  if [[ -z "$name" || "$name" == */* || "$name" == .* ]]; then
    usage
    return 1
  fi
  if [ ! -d "$SIBLINGS/$name" ]; then
    echo "No sibling named '$name' is installed."
    return 1
  fi
  read -rp "Remove $SIBLINGS/$name? [y/N] " response
  case "$response" in
    [yY][eE][sS]|[yY])
      rm -rf "${SIBLINGS:?}/$name"
      echo "Removed $name. Anything its install.sh set up outside that folder is still there."
      ;;
    *)
      echo "Nothing removed."
      ;;
  esac
}

case "$1" in
  ""|add)        add_sibling "$2" ;;
  list|ls)       list_siblings ;;
  remove|rm)     remove_sibling "$2" ;;
  -h|--help|help) usage ;;
  *)             usage; exit 1 ;;
esac
