#!/bin/bash

# SPDX-FileCopyrightText: 2023 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0

function usage() {
cat << EOF
mkchlog - Create changelog entries for Uyuni packages

Usage: mkchlog [OPTIONS] [MESSAGE]

When called from a subdirectory of any Uyuni package, create a changelog file in
the following format:

    <package_name>.changes.<username>.<feature_name>

If not explicitly specified, fetch username and feature_name from git email's
username part and the current branch name.

With no MESSAGE, open a text editor for manual input.
The default editor can be specified by setting the EDITOR environment variable.

  -f, --feature         set the feature name to use as a filename part
  -u, --username        set the username to use as a filename part
  -r, --remove          remove existing changelog file
  -c, --commit          do a git commit with MESSAGE as the commit message
  -n, --no-wrap         do not wrap automatically the message at 67 characters
  -h, --help            display this help and exit

Uyuni project: <https://github.com/uyuni-project/uyuni>
EOF
}

# Out of the box macOS ships with BSD getopt which doesn't support long args
if [ "$(uname -s)" == "Darwin" ] && (man getopt | grep -i -q "bsd"); then
    echo "Error: This tool requires GNU getopt, but your system is using BSD getopt. Please install GNU getopt and add it to your PATH."
    exit 1
fi

if ! ARGS=$(getopt -n mkchlog -o rhncf:u: --long remove,help,no-wrap,commit,feature:username: -- "$@"); then
    exit 1
fi

eval set -- "$ARGS"
while [ $# -gt 0 ]; do
    case "$1" in
        -h|--help)
            usage
            exit
            ;;
        -f|--feature)
            FEATURE=$2
            shift 2
            ;;
        -u|--username)
            USER=$2
            shift 2
            ;;
        -r|--remove)
            REMOVE=1
            shift
            ;;
        -n|--no-wrap)
            NO_WRAP=1
            shift
            ;;
        -c|--commit)
            COMMIT=1
            shift
            ;;
        --)
            shift
            break
            ;;
    esac
done

if ! command -v git &>/dev/null; then
    echo "Error: git is not available in your system."
    exit 1
fi

if [[ -z "$1" && -n "$COMMIT" ]]; then
    echo "'--commit' option can only be used if the MESSAGE argument is provided."
    exit 1
fi


if ! GITROOT=$(git rev-parse --show-toplevel 2>/dev/null); then
    echo "Error: not in a git repository."
    exit 1
fi

PKG_DIR=$(ls -d "$GITROOT"/{.tito,rel-eng}/packages 2>/dev/null)
if [ -z "$PKG_DIR" ] ; then
    echo "Error: Not in Uyuni working directory."
    exit 1
fi

CURDIR=$(git rev-parse --show-prefix) || exit 1

if [ -z "$USER" ]; then
    GITMAIL=$(git config --get user.email 2>/dev/null)
    if [ -n "$GITMAIL" ]; then
        USER=${GITMAIL%@*}
    else
        echo "Cannot read the username from git config. Omitting the username part."
    fi
fi

if [ -z "$FEATURE" ]; then
    FEATURE=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-')
    if [ -z "$FEATURE" ] || [ "HEAD" == "$FEATURE" ]; then
        echo "Cannot read the branch name from the current HEAD. Omitting the feature name part."
        unset FEATURE
    fi
fi

if [ -z "$USER" ] && [ -z "$FEATURE" ]; then
    echo "Error: Neither username nor branch name could be read. Please specify the values using --feature and --username options or create a changelog file manually."
    exit 1
fi

# Returns the changelog file name in format <package>.changes.<username>.<feature>.
# Loops through the base directories of each package and tries to match one with the current directory.
# Returns 1 if no match found, which means the user is outside of a package directory.
function getChangelogFile() {
    for pkg in $(cat "$PKG_DIR"/* | cut -d' ' -f2)
    do
        if [ "$pkg" = "./" ]; then
            pkg=""
        fi

        if echo "$CURDIR" | grep -q "^$pkg"; then
            local chfile
            
            chfile=$(ls "$GITROOT"/"$pkg"*.changes)
            echo "$chfile${USER:+.$USER}${FEATURE:+.$FEATURE}"

            return
        fi
    done
    exit 1
}

# Get user's default text editor, falling back to vi.
function getEditorCmd() {
    local cmd
    if [ -n "$EDITOR" ]; then
        cmd=$EDITOR
    elif command -v vim &>/dev/null; then
        cmd="vim"
    else
        cmd="vi"
    fi

    # Specific CLI options for common text editors
    case "$cmd" in
        vi*)
            cmd="$cmd +1 +startinsert!"
            ;;
        *)
            cmd="$cmd +1:3"
            ;;
    esac

    echo "$cmd"
}

if ! CHFILE=$(getChangelogFile); then
    echo "Error: Not in a package directory."
    exit 1
fi

# Remove option
if [ -n "$REMOVE" ]; then
    if [ -f "$CHFILE" ]; then
        git restore --staged "$CHFILE" 2>/dev/null
        rm "$CHFILE" 2>/dev/null
        exit
    else
        echo "Error: '$CHFILE' does not exist."
        exit 1
    fi
fi

# Add the new entry
if [ -z "$NO_WRAP" ]; then
    echo "$1" | xargs | fold -s -w 65 | sed '1s/^\(.*\S\)\s*$/- \1/;2,$s/^\(.*\S\)\s*$/  \1/' > "$CHFILE.new"
else
    echo "$1" | xargs | sed 's/^/- /' > "$CHFILE.new"
fi

# Append older entries
cat "$CHFILE" >> "$CHFILE.new" 2>/dev/null

# Open file for edit
if [ -z "$1" ]; then
    $(getEditorCmd) "$CHFILE.new"
fi

# Move file into place
if [ -s "$CHFILE.new" ]; then
    mv "$CHFILE.new" "$CHFILE"
    # Stage in git
    git add "$CHFILE"
    if [[ -n "$COMMIT" && -n "$1" ]]; then
        git commit -m "$1"
    fi
else
    # Unstage and remove
    echo "No entries written. Discarding the changelog file."
    git restore --staged "$CHFILE" 2>/dev/null
    rm "$CHFILE.new" "$CHFILE" 2>/dev/null
fi
