#!/usr/bin/env bash
#
# panama – Helper for managing the Panama dotfiles/repo
# Version 1.0
# Author: Gabriel Brown
#
# Commands:
#   update   Commit & sync local changes (or just pull if clean)
#   edit     Open the Panama repo in Neovim
#   help     Show this help
#
# Designed to grow: add new subcommands as cmd_<name> functions and
# register them in the dispatcher / usage block below.

set -euo pipefail

PROGRAM=$(basename "$0")
VERSION="1.0"

# ----------------------------------------------------------------------------
# Locate the Panama repo (this script lives in <repo>/bin/panama)
# ----------------------------------------------------------------------------
SCRIPT_PATH=$(readlink -f "${BASH_SOURCE[0]}")
PANAMA_DIR=$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)

# ----------------------------------------------------------------------------
# Pretty output
# ----------------------------------------------------------------------------
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ $(tput colors 2>/dev/null || echo 0) -ge 8 ]]; then
  BOLD=$(tput bold); RESET=$(tput sgr0)
  RED=$(tput setaf 1); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3)
  BLUE=$(tput setaf 4); MAGENTA=$(tput setaf 5); CYAN=$(tput setaf 6)
else
  BOLD=""; RESET=""; RED=""; GREEN=""; YELLOW=""; BLUE=""; MAGENTA=""; CYAN=""
fi

info()  { printf '%s==>%s %s\n'  "${BLUE}${BOLD}"   "$RESET" "$*"; }
ok()    { printf '%s✓%s %s\n'    "${GREEN}${BOLD}"  "$RESET" "$*"; }
warn()  { printf '%s!%s %s\n'    "${YELLOW}${BOLD}" "$RESET" "$*"; }
err()   { printf '%s✗%s %s\n'    "${RED}${BOLD}"    "$RESET" "$*" >&2; }
header(){ printf '\n%s%s%s\n'    "${MAGENTA}${BOLD}" "$*" "$RESET"; }

# Ask a yes/no question. Returns 0 for yes, 1 for no. Default = no.
confirm() {
  local prompt="$1" reply
  printf '%s?%s %s %s[y/N]%s ' "${CYAN}${BOLD}" "$RESET" "$prompt" "$BOLD" "$RESET"
  read -r reply || true
  [[ "$reply" =~ ^[Yy]([Ee][Ss])?$ ]]
}

# ----------------------------------------------------------------------------
# Usage
# ----------------------------------------------------------------------------
usage() {
  cat <<EOF
${BOLD}$PROGRAM${RESET} – manage the Panama repo (${PANAMA_DIR})

${BOLD}Usage:${RESET}
  $PROGRAM <command> [options]

${BOLD}Commands:${RESET}
  ${GREEN}update${RESET}      Review, commit & sync local changes. If the working tree is
              clean it simply runs 'git pull'.
  ${GREEN}edit${RESET}        Open the Panama repo in Neovim.
  ${GREEN}help${RESET}        Show this help (also -h, --help).

${BOLD}Options:${RESET}
  -h, --help      Show this help and exit
      --version   Show version and exit

${BOLD}Examples:${RESET}
  $PROGRAM update
  $PROGRAM edit
EOF
}

# ----------------------------------------------------------------------------
# Guard: make sure we're in a git repo
# ----------------------------------------------------------------------------
require_git_repo() {
  if ! git -C "$PANAMA_DIR" rev-parse --git-dir >/dev/null 2>&1; then
    err "'$PANAMA_DIR' is not a git repository."
    exit 1
  fi
}

# ----------------------------------------------------------------------------
# Command: update
# ----------------------------------------------------------------------------
cmd_update() {
  require_git_repo
  cd "$PANAMA_DIR"

  info "Panama repo: ${BOLD}${PANAMA_DIR}${RESET}"

  # Any changes in the working tree? (modified, staged, or untracked)
  if [[ -z "$(git status --porcelain)" ]]; then
    info "Working tree is clean — pulling latest changes."
    if git pull --ff-only; then
      ok "Already in sync."
    else
      err "git pull failed."
      exit 1
    fi
    return
  fi

  # Show what changed
  header "Changed files"
  git -c color.status=always status --short

  header "Diff"
  # Tracked changes (staged + unstaged) relative to the last commit.
  git --no-pager -c color.diff=always diff HEAD

  # Untracked files won't appear in 'git diff', so show them as new files.
  local untracked
  untracked=$(git ls-files --others --exclude-standard)
  if [[ -n "$untracked" ]]; then
    while IFS= read -r f; do
      [[ -z "$f" ]] && continue
      git --no-pager -c color.diff=always diff --no-index -- /dev/null "$f" || true
    done <<< "$untracked"
  fi

  echo
  if ! confirm "Commit these changes?"; then
    warn "Aborted — no changes committed."
    return
  fi

  # Commit message
  local msg
  printf '%s?%s Commit message: ' "${CYAN}${BOLD}" "$RESET"
  read -r msg || true
  if [[ -z "${msg// }" ]]; then
    msg="Update $(date '+%Y-%m-%d %H:%M:%S')"
    warn "No message given — using: ${BOLD}${msg}${RESET}"
  fi

  # Is the local branch up to date with its upstream?
  info "Checking whether the repo is up to date..."
  if git rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
    git fetch --quiet
    local local_rev remote_rev base_rev
    local_rev=$(git rev-parse @)
    remote_rev=$(git rev-parse '@{u}')
    base_rev=$(git merge-base @ '@{u}')

    if [[ "$local_rev" == "$remote_rev" ]]; then
      ok "Repo is up to date."
    elif [[ "$local_rev" == "$base_rev" ]]; then
      warn "Repo is behind upstream — stashing, pulling, then re-applying."
      info "Stashing local changes..."
      git stash push --include-untracked -m "panama-update-$(date +%s)" >/dev/null

      if ! git pull --ff-only; then
        err "git pull failed — restoring your changes."
        git stash pop || true
        exit 1
      fi

      info "Re-applying stashed changes..."
      if ! git stash pop; then
        err "Conflict while re-applying changes. Resolve it, then commit manually."
        exit 1
      fi
    else
      warn "Local branch has diverged from upstream — committing locally only."
    fi
  else
    warn "No upstream configured for this branch — committing locally only."
  fi

  # Commit everything
  info "Committing changes..."
  git add -A
  git commit -m "$msg"
  ok "Committed: ${BOLD}${msg}${RESET}"

  echo
  if confirm "Push the changes now?"; then
    info "Pushing..."
    if git push; then
      ok "Pushed to remote."
    else
      err "git push failed."
      exit 1
    fi
  else
    info "Done — changes committed locally but not pushed."
  fi
}

# ----------------------------------------------------------------------------
# Command: edit
# ----------------------------------------------------------------------------
cmd_edit() {
  if ! command -v nvim >/dev/null 2>&1; then
    err "Neovim (nvim) is not installed or not on PATH."
    exit 1
  fi
  info "Opening ${BOLD}${PANAMA_DIR}${RESET} in Neovim."
  cd "$PANAMA_DIR"
  exec nvim .
}

# ----------------------------------------------------------------------------
# Dispatcher
# ----------------------------------------------------------------------------
main() {
  local cmd="${1:-}"
  case "$cmd" in
    update)        shift; cmd_update "$@" ;;
    edit)          shift; cmd_edit "$@" ;;
    help|-h|--help|"") usage ;;
    --version)     printf '%s %s\n' "$PROGRAM" "$VERSION" ;;
    *)
      err "Unknown command: '$cmd'"
      echo
      usage
      exit 1
      ;;
  esac
}

main "$@"
