#!/usr/bin/env bash
set -euo pipefail

name="fetchpijul"
remote=
channel="main"
change=
state=
date=
exp_hash=

usage() {
    echo "Usage: nix-prefetch-pijul [options] [REMOTE] [STATE [EXPECTED-HASH]]"
    echo
    echo "Options:"
    echo "  --name <NAME>        Symbolic store path name to use for the result."
    echo "  --remote <REMOTE>    URL for the Pijul repository."
    echo "  --change <CHANGE>    Clone a specific change."
    echo "  --state <STATE>      Clone a specific state."
    echo "  --channel <CHANNEL>  Channel name (default: ‘main’)."
    echo "  --hash <HASH>        Expected hash."
    echo "  --help               Show this help message."
}

# Argument parsing
while [ $# -gt 0 ]; do
    case "$1" in
        --name)
            name="$2"; shift 2 ;;
        --remote)
            remote="$2"; shift 2 ;;
        --channel)
            channel="$2"; shift 2 ;;
        --change)
            change="$2"; shift 2 ;;
        --state)
            state="$2"; shift 2 ;;
        --hash)
            exp_hash="$2"; shift 2 ;;
        --help)
            usage; exit 0 ;;
        *)
            # Positional arguments
            if [ -z "$remote" ]; then
                remote="$1"
                shift
            elif [ -z "$state" ]; then
                state="$1"
                shift
            elif [ -z "$exp_hash" ]; then
                exp_hash="$1"
                shift
            else
                echo "Error: Too many arguments" >&2
                usage
                exit 1
            fi
            ;;
    esac
done

if [ -z "$remote" ]; then
    echo "Error: URL for remote is required." >&2
    echo >&2
    usage
    exit 1
elif [ -n "$change" -a -n "$state" ]; then
    echo "Error: Only one of ‘change’ or ‘state’ can be set" >&2
    echo >&2
    usage
    exit 1
fi

hash=
hash_algo="${NIX_HASH_ALGO:-"sha256"}"
hash_format="${hashFormat:-"--base32"}"
final_path=

# If the hash was given, a file with that hash may already be in the
# store.
if [ -n "$exp_hash" ]; then
    final_path=$(nix-store --print-fixed-path --recursive "$hash_algo" "$exp_hash" "$name")
    if ! nix-store --check-validity "$final_path" 2> /dev/null; then
        final_path=""
    fi
    hash="$exp_hash"
fi

# If we don’t know the hash or a path with that hash doesn’t exist,
# download the file and add it to the store.
if [ -z "$final_path" ]; then
    tmp_clone="$(realpath "$(mktemp -d --tmpdir pijul-clone-tmp-XXXXXXXX)")"
    trap "rm -rf \"$tmp_clone\"" EXIT

    clone_args="--channel $channel"
    if [ -n "$change" ]; then
        clone_args="$clone_args --change $change"
    elif [ -n "$state" ]; then
        clone_args="$clone_args --state $state"
    fi

    cd "$tmp_clone"
    pijul clone $clone_args "$remote" "$name"
    latest_log="$(pijul log --repository "$tmp_clone/$name" --limit 1)"
    # State is a stable reference is a stable reference for the patchset that
    # doesn't depend on patch order. As a result, it will always be included.
    if [ -z "$state" ]; then
        state="$(printf "%s" "$latest_log" | awk '/^State:/ {printf $2}')"
    fi
    date="$(printf "%s" "$latest_log" | awk '/^Date:/ { sub(/^Date:[[:space:]]*/, "", $0); printf $0 }')"
    date="$(date -u -d "$date" "+%Y-%m-%dT%H:%M:%SZ")"

    rm -rf "$tmp_clone/$name/.pijul"

    hash="$(nix-hash --type "$hash_algo" "$hash_format" "$tmp_clone/$name")"
    final_path=$(nix-store --add-fixed --recursive "$hash_algo" "$tmp_clone/$name")

    if [ -n "$exp_hash" -a "$exp_hash" != "$hash" ]; then
        echo "Hash mismatch for “$remote” @ “$channel”" >&2
        echo "Expected: $exp_hash" >&2
        echo "Got:      $hash" >&2
        exit 1
    fi
fi

json_escape() {
    printf '%s' "$1" | jq -Rs .
}

cat <<EOF
{
    "remote": $(json_escape "$remote"),
    "channel": $(json_escape "$channel"),
EOF
if [ -n "$change" ]; then cat <<EOF
    "change": $(json_escape "$change"),
EOF
fi; cat <<EOF
    "state": $(json_escape "$state"),
    "date": $(json_escape "$date"),
    "path": "$final_path",
    $(json_escape "$hash_algo"): $(json_escape "$hash"),
    "hash": "$(nix-hash --to-sri --type "$hash_algo" "$hash")"
}
EOF
# vim: noet ci pi sts=0
