#!/bin/bash

# GIT FUNCTIONS

# git checkout with tab completion
# bash override for zsh gco alias
if [ -n "$BASH_VERSION" ] && [ -f ~/.config/aliases/.git_bash_autocomplete ]; then
  . ~/.config/aliases/.git_bash_autocomplete
fi

# git create stast with message
git_stash_msg() {
  if [ -z "$1" ]; then
    e_warn "Stash changes with message! Don't be lazy..."
  else
    local msg="$*"
    git stash -m "$msg"
    e_succ "Succesfully stashed changes as '$msg' on branch '$(git branch --show-current)'."
  fi
}

# git apply stash by number
git_apply_stash() {
  if [ $# -eq 0 ]; then
    e_warn "Please specify stash number!"
    return 1
  elif [ $# -gt 1 ]; then
    e_warn "Please specify only one stash number!"
    return 1
  else
    git stash apply stash@"{$1}"
  fi
}

# git echo stasth diff by number
# useful combined with '| xc'
git_stash_patch() {
  if [ $# -eq 0 ]; then
    e_warn "Please specify a stash number!"
    return 1
  elif [ $# -gt 1 ]; then
    e_warn "Please specify only one stash number!"
    return 1
  else
    git stash show -p stash@"{$1}"
  fi
}

# drop stash by number with confirmation
# I accidentally dropped stashes, because I mix up gsp and gsd
git_stash_drop() {
  if [ $# -eq 0 ]; then
    e_warn "Please specify a stash number!"
    return 1
  elif [ $# -gt 1 ]; then
    e_warn "Please specify only one stash number!"
    return 1
  else
    printf "Are you sure you want to drop stash #$1? (Y/N): "
    read -r confirm
    if [[ "$confirm" =~ ^[yY]$ ]]; then
      git stash drop stash@"{$1}"
      e_info "Stash #$1 dropped."
    else
      e_warn "Aborted. Stash #$1 not dropped."
    fi
    return 0
  fi
}

# git diff with optional filename from git diff --names-only | nl
git_diff_numbered() {
  local files file
  files=()
  while IFS= read -r line; do
    files+=("$line")
  done < <(git diff --name-only)
  if [ $# -eq 0 ]; then
    printf "%s\n" "${files[@]}" | nl
  elif [ $# -eq 1 ] && [[ $1 =~ ^[0-9]+$ ]]; then
    if [ -n "$BASH_VERSION" ]; then
      file="${files[$1 - 1]}"
    else
      file="${files[$1]}"
    fi
    [ -n "$file" ] && git diff "$file"
  else
    e_err "Invalid argument! Provide number from the output of 'gdn'."
  fi
}

# git restore file based on the number from git diff --names-only | nl
git_restore_by_number() {
  local files file
  files=()
  while IFS= read -r line; do
    files+=("$line")
  done < <(git diff --name-only)
  if [ $# -eq 0 ]; then
    e_err "Missing argument! Provide number from the output of 'gdn'."
  elif [ $# -eq 1 ] && [[ $1 =~ ^[0-9]+$ ]]; then
    if [ -n "$BASH_VERSION" ]; then
      file="${files[$1 - 1]}"
    else
      file="${files[$1]}"
    fi
    [ -n "$file" ] && git restore "$file"
  else
    e_err "Invalid argument! Provide number from the output of 'gdn'."
  fi

}

# git add untracked files, commit and push
git_commit_push() {
  if [ -z "$1" ]; then
    e_err "No commit message provided!"
    return 1
  else
    local message="$*"
    git add -u
    git commit -m "$message"
    git push
    e_succ -e "\nCommit '$message' succesfully pushed to branch: '$(git branch --show-current)'."
  fi
}

# git list stashes
git_stash_list() {
  printf "%s\n" "$(git stash list)"
}

### Git aliases
# git status
fake_alias gst
if [ -n "$BASH_VERSION" ]; then
  alias gst='git status'
fi
# git fetch all
fake_alias gfa
if [ -n "$BASH_VERSION" ]; then
  alias gfa='git fetch --all --prune'
fi
# git diff
fake_alias gd
if [ -n "$BASH_VERSION" ]; then
  alias gd='git diff'
fi
# git diff classic with less pager
alias gdc="git -c core.pager='less' diff"
# git diff numbered
alias gdn=git_diff_numbered
# (with filenum works only in repo root)
next_line
# git commit and push with message
alias gcpm=git_commit_push
# git checkout with tab completion
fake_alias gco
# git submodule update
fake_alias gsu
if [ -n "$BASH_VERSION" ]; then
  alias gsu='git submodule update'
fi
# git stash with message
alias gstm=git_stash_msg
# git stash list
alias gstl=git_stash_list
# git stash patch <#>
alias gstp=git_stash_patch
# git stash apply <#>
alias gsta=git_apply_stash
# gti stash drop <#>
alias gstd=git_stash_drop
# git restore
alias grs='git restore'
# git restore by number from gdn
alias grsn=git_restore_by_number
# run pre-commit on changed files only
alias pc='pre-commit run --files $(git diff --name-only)'
