#! /usr/bin/env dash

# shellcheck source=/dev/null
SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH

UPDATE_SCRIPT_NAME="update.sh"
CONFIG_DIRECTORY_PATH="$HOME/.local/share/flake-update"

# Both are used in version()
# shellcheck disable=SC2034
AUTHORS="Soispha"
# shellcheck disable=SC2034
YEARS="2023"

UPDATE_SCRIPT_NOT_WANTED=false

# Searches upward for a `UPDATE_SCRIPT_NAME` script
# Returns a path to the script if it exists, otherwise nothing is returned
check_for_update_script() {
    dirname="$(search_upward_files "$UPDATE_SCRIPT_NAME")"
    if [ "$dirname" ]; then
        printf "%s/%s" "$dirname" "$UPDATE_SCRIPT_NAME"
    fi
}

# Checks if a given path to the update script is allowed.
# Takes the path as input
# Return 0, if allowed, 1 if not.
check_for_allowed_update_script() {
    update_script="$1"
    config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
    update_script_hash="$(sha256sum "$update_script")"
    if [ -f "$config_path" ]; then
        if [ "$(cat "$config_path")" = "$update_script_hash" ]; then
            dbg "Recorded hash matches"
            return 0
        else
            dbg "Recorded hash \'$(cat "$config_path")\' does not match real hash \'$update_script_hash\', assuming not allowed"
            return 1
        fi
    else
        dbg "Path \'$config_path\' does not exist, assuming not allowed"
        return 1
    fi
}

# Asks the user if they want to allow a given script.
# Takes the path as input
ask_to_allow_update_script() {
    update_script="$1"
    config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
    update_script_hash="$(sha256sum "$update_script")"
    println "\033[2J" # clear the screen
    cat "$update_script"
    readp "Do you want to allow this script?[N/y]: " allow
    # shellcheck disable=SC2154
    dbg "allow is: $allow"
    case "$allow" in
    [yY])
        dbg "allowed script"
        dbg "storing contents in: $config_path"
        mkdir --parents "$(dirname "$config_path")"
        print "$update_script_hash" >"$config_path"
        ;;
    *)
        UPDATE_SCRIPT_NOT_ALLOWED=true
        ;;
    esac
}

# Runs the provided script and continues to update the nix flake
# Takes the path to the script and the directory to the flake as arguments
# If the path to the update script is empty, it will be ignored
update() {
    update_script="$1"
    flake_base_dir="$2"
    shift 2
    dbg "Provided following args to update script: '$*'"

    cd "$flake_base_dir" || die "Provided dir \'$flake_base_dir\' can not be accessed"
    dbg "changed directory to: $flake_base_dir"

    nix flake update

    if ! [ "$update_script" = "" ] && ! [ "$UPDATE_SCRIPT_NOT_WANTED" = "true" ]; then
        "$update_script" "$@"
    fi

    if grep '[^0-9]_[0-9]' flake.lock >/dev/null; then
        batgrep '[^0-9]_[0-9]' flake.lock
        die "Your flake.nix contains duplicate inputs!"
    fi
}

help() {
    cat <<EOF
This is a Nix flake update manager.

USAGE:
    $NAME [--help | --version] [flake [--no-script] | <some other command>]

OPTIONS:
    --help   | -h
                            Display this help and exit.

    --version   | -v
                            Display version and copyright information and exit.

    --no-script
                            Avoid running the 'update.sh' script
COMMANDS:
    flake
                            update the flake project

    <some other command>
                            runs a executable called "update-<some other command>", if it exists
EOF
}

main() {
    if ! [ "$UPDATE_SCRIPT_NOT_ALLOWED" = true ]; then
        update_script="$(check_for_update_script)"
        flake_base_dir="$(search_flake_base_dir)" # Assume, that the update script is in the base dir
        dbg "update_script is: $update_script"
        dbg "flake_base_dir is: $flake_base_dir"

        if [ "$update_script" = "" ]; then
            update "" "$flake_base_dir" "$@"
        elif check_for_allowed_update_script "$update_script" && ! [ "$update_script" = "" ]; then
            update "$update_script" "$flake_base_dir" "$@"
        else
            ask_to_allow_update_script "$update_script"
            main "$@"
        fi
    fi
}

if [ "$#" -eq 0 ]; then
    main
    exit 0
fi

for input in "$@"; do
    case "$input" in
    "--help" | "-h")
        help
        exit 0
        ;;
    "--version" | "-v")
        version
        exit 0
        ;;
    "--no-script" | "-n")
        UPDATE_SCRIPT_NOT_WANTED=true
        ;;
    "--")
        end_of_cli_options=true

        # Stop processing args after that marker.
        break
        ;;
    esac
    [ "$end_of_cli_options" = "true" ] && break
done

case "$1" in
"flake")
    shift 1

    # Filter out fupdate specific flags
    while [ "$1" != "--" ]; do
        # FIXME: This check allows to add a flag multiple times, but this should probably
        # not be allowed <2024-03-29>
        case "$1" in
        "--no-script" | "-n")
            shift 1
            ;;
        *)
            break
            ;;
        esac
    done

    [ "$1" = "--" ] && shift 1
    main "$@"
    ;;
*)
    command="$1"
    shift 1
    [ "$1" = "--" ] && shift 1
    if which update-"$command" >/dev/null 2>&1; then
        update-"$command" "$@"
    else
        die "command \"update-$command\" is not executable, or does not exist"
    fi
    ;;
esac