#! /usr/bin/env bash
# shellcheck disable=SC2317
# SC2317: We use eval, things are used.

# boilerplate
THIS="$(basename "$0")" # 'release'
LAST_DIR="$PWD"
cd "$(dirname "$0")/.." || exit 1
LAST_BRANCH="$(git branch --show-current)"

# htor: human version to regular expression
release::htor() {
  case "$1" in
    maj|major)  local PART='\1';;
    min|minor)  local PART='\2';;
    mic|micro)  local PART='\3';;
    full)       local PART='\1.\2.\3';;
    *)          local PART='\1.\2.\3';;
  esac
  echo "$PART"
}

release::show() {
  PART="$(release::htor "$1")"
  git tag --list \
    | sort -r \
    | head -n 1 \
    | sed -E "s|release/([0-9]+)\.([0-9]+)\.([0-9]+)|$PART|g"
}

release::show::part() {
  test $# -ne 1 && return 1
  PART="$(release::htor "$1")"
  git tag --list \
    | sort -r \
    | head -n 1 \
    | sed -E "s|release/([0-9]+)\.([0-9]+)\.([0-9]+)|$PART|g"
}

release::show::all() {
  PART="$(release::htor full)"
  git tag --list \
    | sort -r \
    | sed -E "s|release/([0-9]+)\.([0-9]+)\.([0-9]+)|$PART|g"
}

release::show::advance() {
  test $# -ne 1 && return 1

  MAJ=$(release::show::part maj)
  MIN=$(release::show::part min)
  MIC=$(release::show::part mic)

  case "$1" in
    maj|major)  echo "$((MAJ + 1)).0.0";;
    min|minor)  echo "$MAJ.$((MIN + 1)).0";;
    mic|micro)  echo "$MAJ.$MIN.$((MIC + 1))";;
    *)          return 1;;
  esac
}

files-with-versions() {
  echo 'CMakeLists.txt
flake.nix
meson.build
pyproject.toml
watcher-py/watcher/watcher.py
Cargo.toml'
}

release::create() {
  local SEMVER="$1"
  if echo "$SEMVER" | grep -qE -- '^[0-9]+\.[0-9]+\.[0-9]+$'
  then
    test "$LAST_BRANCH" = 'next' \
      || git checkout next

    echo "$SEMVER" > .version
    git add '.version'

    files-with-versions \
    | while read -r f
      do
        sed \
          -E -e "s|( +)([\"']?)([0-9]+\.[0-9]+\.[0-9]+)(.+# hook: tool/release)|\1\2$SEMVER\4|" \
          -i -- "$f"
        git add "$f"
        [ -e "$f--" ] && rm "$f--"
      done

    git commit -m "release: $SEMVER"
    git push

    git checkout release
    git merge next -m "release/$SEMVER" \
      && git push \
      && git tag \
        -a "release/$SEMVER" \
        -m "release/$SEMVER" \
        "$(git log | head -n 1 | sed -E 's/commit (.*)/\1/g')" \
      && git push origin "release/$SEMVER" \
      && git checkout "$LAST_BRANCH"
    return $?
  else
    return 1
  fi
}

release::help() {
echo "
  synopsis:
    $0 < --help | --create < version > | --show [ part | --all | --advance part ] >

  example:
    $0 --help
    $0 --show
    $0 --show major
    $0 --show minor
    $0 --show micro
    $0 --show --all
    $0 --create 1.2.3
    $0 --show --advance maj
    $0 --show --advance min
    $0 --show --advance mic
    $0 --create \`$0 --show --advance mic\`

  ebnf: 
    calling convention =
      '$0 ' , argument

    argument =
        '--help'
      | '--create ' , semantic version
      | '--show '   [ '--advance ' , part
                    | '--all'
                    | part ]

    part =
        'minor' | 'major' | 'micro'
      | 'maj'   | 'min'   | 'mic' ;

    positive integer =
      { '0' | '1' | '2' | '3' | '4'
      | '5' | '6' | '7' | '8' | '9' } ;

    semantic version =
      positive integer ,
      '.' , positive integer ,
      '.' , positive integer ;
"
}

release() {
  if echo "$1" | grep -qE -- '^--.*'
  then
    local CMDB="$THIS"
    for ARG in "$@"; do
      case "$ARG" in
        # command
        --*)  CMDB+="::${ARG##--}";;
        # argument
        *)    CMDB+=" ${ARG}";;
      esac
    done
    ${CMDB[@]}
    return $?
  else
    return 1
  fi
}

release $@

# boilerplate
EXIT_VAL=$?
test $EXIT_VAL -gt 0 \
  && "$THIS::help"
cd "$LAST_DIR"
exit $EXIT_VAL
