#!/usr/bin/env bash
# This script is intended to be run at the root of your local clone
# Example usage: 
#   For interactive usage:
#   ./git_sparse_push_interactive.sh -r upstream -b v1.0.10.dev6/corona
#
#   For non-interative usage: 
#   ./git_sparse_push_interactive.sh -r upstream -b v1.0.10.dev6/corona -d test_app3_v100,test_app4_v100
#  !! For non-interactive usage, please make sure to not include spaces between names of dirs
#     as shown above

set -euo pipefail
IFS=$'\n\t'

# git-sparse-push-interactive.sh
# Interactive script to add directories to sparse-checkout, commit, and push.

usage() {
  cat <<EOF
Usage: $0 -r <remote> -b <branch> [-d <dir1[,dir2,...]>]

  -r   Name of the git remote (e.g., origin, upstream)
  -b   Branch name to push to (e.g., v1.0.10.dev7/corona)
  -d   Optional comma-separated or space-separated list of directories
       to push. If omitted, you will be prompted interactively.
EOF
  exit 1
}

#parse options
remote=""
branch=""
declare -a dirs

while getopts ":r:b:d:" opt; do
  case $opt in
    r) remote="$OPTARG" ;;
    b) branch="$OPTARG" ;;
    d) 
      if [[ "$OPTARG" == *","* ]]; then
        IFS=',' read -ra dirs <<< "$OPTARG"
      else
        dirs=("$OPTARG")
      fi
      ;;
    :) echo "Error: Option -$OPTARG requires an argument." >&2; usage ;;
    \?) echo "Error: Invalid option -$OPTARG" >&2; usage ;;
  esac
done

#check mandatory args
if [[ -z "$remote" || -z "$branch" ]]; then
  usage
fi

# Interactive selection if dirs not provided
if [ ${#dirs[@]} -eq 0 ]; then
  # Interactive selection: list top-level dirs excluding .git and whatever is hidden
  mapfile -t all_dirs < <(
    find . -type d \
      | sed -e 's|^\./||' \
      | awk -F/ 'NF==1 && $1!="" && $1!=".git" {print}'
  )
  if [ ${#all_dirs[@]} -eq 0 ]; then
    echo "No directories found in current working directory." >&2
    exit 1
  fi

  echo "Select directories to push (comma-separated indices), or 'all':"
  for i in "${!all_dirs[@]}"; do
    printf "%3d) %s\n" "$((i+1))" "${all_dirs[i]}"
  done
  printf "%3d) %s\n" "$(( ${#all_dirs[@]} + 1 ))" "all"

  read -rp "Enter choice(s): " choice
  if [[ "$choice" == "all" ]]; then
    dirs=("${all_dirs[@]}")
  else
    # Split on commas or whitespace
    if [[ "$choice" == *","* ]]; then
      IFS=',' read -ra temp_indices <<< "$choice"
    else
      read -ra temp_indices <<< "$choice"
    fi
    for raw in "${temp_indices[@]}"; do
      idx=$(echo "$raw" | xargs)
      # Skip empty tokens
      if [ -z "$idx" ]; then
        continue
      fi
      if ! [[ "$idx" =~ ^[0-9]+$ ]]; then
        echo "Invalid index: '$raw'" >&2
        exit 1
      fi
      if (( idx >= 1 && idx <= ${#all_dirs[@]} )); then
        dirs+=("${all_dirs[idx-1]}")
      else
        echo "Index out of range: $idx" >&2
        exit 1
      fi
    done
  fi
fi

#verify git repository
if ! git rev-parse --git-dir &>/dev/null; then
  echo "Error: Not a git repository." >&2
  exit 1
fi

#bail if remote does not exist
if ! git remote get-url "$remote" &>/dev/null; then
  echo "Error: Remote '$remote' not found." >&2
  exit 1
fi

#fetch and checkout branch
git fetch "$remote" "$branch"
git checkout -B "$branch" "$remote/$branch"

#initialize sparse-checkout if needed
git sparse-checkout init --cone 2>/dev/null || true

#add directories to sparse-checkout
for d in "${dirs[@]}"; do
  git sparse-checkout add "$d"
done

#stage and commit changes
git add "${dirs[@]}"
if git diff --cached --quiet; then
  echo "No changes to commit for: ${dirs[*]}"
else
  git commit -m "Add directories: ${dirs[*]}"
fi

#push to remote
git push "$remote" "$branch"

echo "Pushed directories: ${dirs[*]} to $remote/$branch"

