#!/usr/bin/bash

set -eo pipefail

OPTS=()
SPEC=
while IFS= read -r line; do
    if [ "$line" = "---" ]; then
        break
    fi
    case "$line" in
        --enablerepo=*|\
        --disablerepo=*|\
        --repoid=*|\
        --releasever=*|\
        --refresh)
            OPTS+=("$line")
            ;;
        *)
            SPEC="$line"
            ;;
    esac
done

repodir=$(mktemp -d)
trap 'rm -r "$repodir"' EXIT
cat > "$repodir/template.repo"

OPTS+=("--setopt=reposdir=${repodir}")
OPTS+=("--quiet")

if [ -d '/usr/lib/qubes/dnf-plugins' ]; then
    # use vendored 'download' dnf-plugin when available, to not require
    # dnf-plugins-core package (not available in Debian yet)
    OPTS+=("--setopt=pluginpath=/usr/lib/qubes/dnf-plugins")
fi

if ! command -v dnf >/dev/null; then
    echo "ERROR: dnf command is missing, please use newer template for your UpdateVM to download templates." >&2
    echo "You can choose any Fedora version, Debian 11 (or newer), or any other based on those (like Whonix 16)." >&2
    exit 1
fi

# This creates the hashfile if it doesn't exist, and keep the ctime and mtime
# unchanged otherwise.
# We then copy the {c,m}time to the repo config.
# This allows DNF caching to work properly.
hashfile="/tmp/qvm-template-$(b2sum "$repodir/template.repo" | cut -f1 -d' ')"
touch -a "$hashfile"
touch -r "$hashfile" "$repodir/template.repo"

RET=0

if [ "$1" = "query" ]; then
    dnf repoquery "${OPTS[@]}" --qf='%{name}|%{epoch}|%{version}|%{release}|%{reponame}|%{downloadsize}|%{buildtime}|%{license}|%{url}|%{summary}|%{description}|' "$SPEC"
    RET="$?"
elif [ "$1" = "download" ]; then
    # Download/retry algorithm: take mirrors in random order. In this order,
    # try to download from the first one - if download failed but anything was
    # downloaded - retry from the same one. If download failed and nothing was
    # downloaded, go to the next one. The intention is to retry on interrupted
    # connection, but skip mirrors that are not synchronized yet.
    # FIXME: 'dnf download --url' prints only one URL, not all the mirrors
    urls="$(dnf download "${OPTS[@]}" --url "$SPEC" | shuf)"
    readarray -t urls <<<"$urls"
    downloaded=0
    status_file="$repodir/download-status.tmp"
    for url in "${urls[@]}"; do
        while true; do
            # pipe data through dd to count bytes for resuming purpose
            if curl --fail --silent --continue-at "$downloaded" -L "$url" -o - |\
                    dd bs=1M 2>"$status_file"; then
                exit 0
            fi
            now_downloaded=$(grep '^[0-9]\+ bytes' "$status_file")
            now_downloaded=${now_downloaded% bytes*}
            if [ -z "$now_downloaded" ] || [ "$now_downloaded" -eq 0 ]; then
                # go to the next mirror
                break
            fi
            downloaded=$(( downloaded + now_downloaded ))
        done
    done
    # ran out of mirrors to try
    RET=1
fi

exit "$RET"
