#!/usr/bin/env bash

# Update shfmt to the latest available version

# A better class of script...
set -o errexit          # Exit on most errors (see the manual)
set -o errtrace         # Make sure any error trap is inherited
set -o nounset          # Disallow expansion of unset variables
set -o pipefail         # Use last non-zero exit code in a pipeline
#set -o xtrace          # Trace the execution of the script (debug)

# Local installation path
SHFMT_DST="$HOME/bin/shfmt"
# Latest release information
SHFMT_LATEST_RELEASE='https://api.github.com/repos/mvdan/sh/releases/latest'

# DESC: Usage help
# ARGS: None
# OUTS: None
function script_usage() {
    cat << EOF
Usage:
     -h|--help                  Displays this help
     -v|--verbose               Displays verbose output
    -nc|--no-colour             Disables colour output
EOF
}

# DESC: Parameter parser
# ARGS: $@ (optional): Arguments provided to the script
# OUTS: Variables indicating command-line parameters and options
function parse_params() {
    local param

    while [[ $# -gt 0 ]]; do
        param="$1"
        shift
        case $param in
            -h | --help)
                script_usage
                exit 0
                ;;
            -v | --verbose)
                verbose=true
                ;;
            -nc | --no-colour)
                no_colour=true
                ;;
            *)
                echo "Invalid parameter was provided: $param"
                exit 1
                ;;
        esac
    done
}

# DESC: Find some important paths we need before script_init() is called
# ARGS: None
# OUTS: $script_real_path: The full canonicalized path to the script
#       $script_real_dir: The canonicalized directory path of the script
#       $script_real_name: The canonicalized file name of the script
#       $dotfiles_path: The dotfiles directory path
#       $dotfiles_name: The dotfiles directory name
# shellcheck disable=SC2034
function early_init() {
    # Determine the physical directory this script resides in. This approach is
    # necessarily clumsy as simple methods like "readlink -f" and "realpath"
    # are not platform independent. We have to work on Linux, BSD, OS X, etc...
    script_real_path="$(perl -MCwd -le 'print Cwd::abs_path(shift)' "${BASH_SOURCE[0]}")"
    script_real_dir="$(dirname "$script_real_path")"
    script_real_name="$(basename "$script_real_path")"
    readonly script_real_path script_real_dir script_real_name

    # Dotfiles directory path & name
    dotfiles_path="$(cd -P "$script_real_dir/../../" && pwd)"
    dotfiles_name="$(basename "$dotfiles_path")"
    readonly dotfiles_path dotfiles_name
}

# DESC: Update to latest shfmt
# ARGS: None
# OUTS: None
function dl_shfmt() {
    local current_ver
    if [[ -x $SHFMT_DST ]]; then
        current_ver="$("$SHFMT_DST" --version | grep -Eo '[0-9]+(\.[0-9]+){1,}')"
        verbose_print "[shfmt] Existing version: v$current_ver"
    else
        verbose_print "[shfmt] Found no existing binary."
    fi

    verbose_print "[shfmt] Retrieving release info ..."
    local metadata latest_ver
    metadata="$(curl -s "$SHFMT_LATEST_RELEASE")"
    latest_ver="$(echo "$metadata" | jq -r '.tag_name' | grep -Eo '[0-9]+(\.[0-9]+){1,}')"

    if [[ $latest_ver == "${current_ver-}" ]]; then
        pretty_print "[shfmt] Latest version is installed: v$current_ver"
        exit 0
    fi

    local kernel_name kernel_name_raw
    kernel_name_raw="$(uname -s)"
    kernel_name="${kernel_name_raw,,}"
    if ! [[ $kernel_name =~ darwin|linux ]]; then
        script_exit "[shfmt] Unsupported kernel: $kernel_name_raw" 1
    fi

    local machine_hw_name machine_hw_name_raw
    machine_hw_name_raw="$(uname -m)"
    case $machine_hw_name_raw in
        i386 | i686)
            machine_hw_name='386'
            ;;
        x86_64)
            machine_hw_name='amd64'
            ;;
        *)
            script_exit "[shfmt] Unsupported machine hardware: $machine_hw_name_raw" 1
            ;;
    esac

    local latest_contains
    latest_contains="${kernel_name}_${machine_hw_name}"
    verbose_print "[shfmt] Filtering for release containing: $latest_contains"

    local latest_url
    latest_url="$(echo "$metadata" | jq -Mr ".assets[] | select(.name | contains(\"$latest_contains\")) | .browser_download_url")"
    if [[ -z $latest_url ]]; then
        script_exit "[shfmt] Unable to find release for kernel & machine hardware: $latest_contains" 1
    fi

    if [[ -n ${current_ver-} ]]; then
        pretty_print "[shfmt] Updating v$current_ver to v$latest_ver ..."
    else
        pretty_print "[shfmt] Installing v$latest_ver ..."
    fi
    curl --create-dirs -sSL -o "$SHFMT_DST" "$latest_url"
    chmod +x "$SHFMT_DST"
}

# DESC: Main control flow
# ARGS: $@ (optional): Arguments provided to the script
# OUTS: None
function main() {
    early_init

    # shellcheck source=scripts/bin/bash-template.sh
    source "$script_real_dir/bash-template.sh"

    trap script_trap_err ERR
    trap script_trap_exit EXIT

    script_init "$@"
    parse_params "$@"
    colour_init

    check_binary curl fatal
    check_binary jq fatal

    dl_shfmt
}

main "$@"

# vim: syntax=sh cc=80 tw=79 ts=4 sw=4 sts=4 et sr
