#!/usr/bin/env bash
# ==========================================================================
#              ____        _ _     _     _____           _
#             | __ ) _   _(_) | __| |   |_   _|__   ___ | |___
#             |  _ \| | | | | |/ _` |_____| |/ _ \ / _ \| / __|
#             | |_) | |_| | | | (_| |_____| | (_) | (_) | \__ \
#             |____/ \__,_|_|_|\__,_|     |_|\___/ \___/|_|___/
#
#                           --- Build-Tools ---
#                https://www.nntb.no/~dreibh/system-tools/
# ==========================================================================
#
# Create weeky Git tags after N days of inactivity
# Copyright (C) 2018-2026 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact: thomas.dreibholz@gmail.com

# Bash options:
set -euo pipefail


# ###### Check whether tag has to be created ################################
make-tag ()
{
   local hash="$1"
   local timestamp="$2"
   local weeklyTagLabel="$3"
   local dryRun="$4"

   if ! git show-ref --quiet --verify "refs/tags/${weeklyTagLabel}" ; then
      tagDate="$(unixts2time --seconds "${timestamp}")"
      if [ "${dryRun}" -eq 0 ] ; then
         # echo "${hash} ${weeklyTagLabel}"
         echo "Creating tag ${hash} (${tagDate}) -> ${weeklyTagLabel}"
         GIT_COMMITTER_DATE="${tagDate}" git tag --annotate --sign --message \
             "Weekly tag ${weeklyTagLabel}." \
             "${weeklyTagLabel}" "${hash}"
      else
         echo "To be created: ${hash} (${tagDate}) -> ${weeklyTagLabel} (dry run only!)"
      fi
   else
      echo "${weeklyTagLabel} already exists"
   fi
}


# ###### Usage ##############################################################
usage () {
   local exitCode="$1"
   echo >&2 "Usage: $0 [--create-tags] [-h|--help] [-v|--version]"
   exit "${exitCode}"
}


# ###### Version ############################################################
version () {
   echo "try-hard @BUILD_MAJOR@.@BUILD_MINOR@.@BUILD_PATCH@"
   exit 0
}


# ###### Main program #######################################################

# ====== Handle arguments ===================================================
if (( BASH_VERSINFO[0] < 4 )) || (( (BASH_VERSINFO[0] == 4) && (BASH_VERSINFO[1] < 4) )) ; then
   echo "ERROR: Bash 4.4 or higher is required, but Bash ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]} is installed!"
   exit 1
fi
GETOPT="$(PATH="/usr/local/bin:${PATH}" command -v getopt)"
if "${GETOPT}" -T >/dev/null 2>&1 || [[ $? -ne 4 ]]; then
   echo >&2 "ERROR: Enhanced/GNU getopt is required!"
   exit 1
fi
options="$(${GETOPT} -o i:hv --long inactivity-days:,create-tags,help,version -a -- "$@")"
# shellcheck disable=SC2181
if [[ $? -ne 0 ]]; then
   usage 1
fi
eval set -- "${options}"


INACTIVITY_DAYS=7
DRY_RUN=1
while [ $# -gt 0 ] ; do
   case "$1" in
      -i | --inactivity-days)
         INACTIVITY_DAYS="$2"
         shift
         if [[ ! "${INACTIVITY_DAYS}" =~ ^([0-9]+)$ ]] || [ "${INACTIVITY_DAYS}" -lt 1 ] ; then
            echo >&2 "ERROR: Invalid inactivity days value ${INACTIVITY_DAYS}!"
            exit 1
         fi
         ;;
      --create-tags)
         DRY_RUN=0
         ;;
      -h | --help)
         usage 0
         ;;
      -v | --version)
         version
         ;;
      --)
         shift
         break
         ;;
      *)
         # This should not happen: wrong getopt parameters, or missing case?
         echo >&2 "INTERNAL ERROR: Unhandled option $1!"
         exit 1
         ;;
  esac
  shift
done
if [ $# -ne 0 ] ; then
   usage 1
fi
INACTIVITY_SECONDS=$((INACTIVITY_DAYS * 86400))


# ====== Check repository ===================================================
# Check if we are inside a valid Git repository
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    echo >&2 "ERROR: This directory is not a Git repository."
    exit 1
fi


# ====== Find weekly tags ===================================================
previousHash=""
previousTimestamp=""
previousWeeklyTagLabel=""

while read -r hash timestamp weeklyTagLabel; do
    # ------ Initialise on first commit -------------------------------------
    if [ -z "${previousHash}" ]; then
        previousHash="${hash}"
        previousTimestamp="${timestamp}"
        previousWeeklyTagLabel="${weeklyTagLabel}"
        continue
    fi

    # ------ Get timestamp difference between current and previous commit ---
    difference=$((timestamp - previousTimestamp))

    # ------ Create tag, if it is before inactivity period ------------------
    if [ "${difference}" -ge "${INACTIVITY_SECONDS}" ] ; then
      make-tag "${previousHash}" "${previousTimestamp}" \
         "${previousWeeklyTagLabel}" "${DRY_RUN}"
    fi

    # ------ Prepare next round ---------------------------------------------
    previousHash="${hash}"
    previousTimestamp="${timestamp}"
    previousWeeklyTagLabel="${weeklyTagLabel}"
done < <(git log --reverse --date=format:"%G.w%V" --format="%H %ct %cd")


# ====== Handle last commit =================================================
if [ -n "${previousHash}" ]; then
   timestamp="$(time2unixts --seconds)"
   difference=$((timestamp - previousTimestamp))
   if [ "${difference}" -ge "${INACTIVITY_SECONDS}" ] ; then
      make-tag "${previousHash}" "${previousTimestamp}" \
         "${previousWeeklyTagLabel}" "${DRY_RUN}"
   fi
fi
