#!/bin/sh

is_root() {
   test $(id -u) = "0"
}

SUDO=
SUDO_INFO_LOGGED=
USERNS_INFO_LOGGED= # set by sh/lib/setup when the tests run in a user namespace
RPM_TESTING_OFF_LOGGED=
RPM_TESTING_OFF_COUNTED= # test that a skip was already counted for (10-booldeps helpers)

sudo_up() {
    [ -n "$SUDO" ] && return

    is_root && return
    command -v sudo 2>&1 >/dev/null || return
    sudo -n rpm --version >/dev/null 2>/dev/null || return

    if [ -z "$SUDO_INFO_LOGGED" ]; then
        msgn "Will use SUDO to test with /bin/rpm"
        SUDO_INFO_LOGGED=1
    fi

    SUDO="sudo"
}

# rpm --root DIR and poldek --install-dist=DIR call chroot(), so we need to be a root/ have
# sudo for such tests.
#
# sh/lib/setup re-runs the test inside a user namespace, where we are uid 0 without being
# root on the host. $SUDO then stays empty, but the test below looks at $(id -u), so these
# tests do run -- and say so when verbose.
is_rpm_testing_off() {
    sudo_up

    if [ -n "$SUDO" ] || [ $(id -u) = "0" ]; then
        # run as root, without root via a userns, or with (passwordless) sudo
        if [ -z "$SUDO" -a "${POLDEK_TEST_USERNS:-}" = "1" -a -z "$USERNS_INFO_LOGGED" ]; then
            force_msg "## rpm tests run without root (user namespace, uid $(id -u))"
            USERNS_INFO_LOGGED=1
        fi
        return 1
    fi

    if [ -z "$RPM_TESTING_OFF_LOGGED" ]; then
        force_msg "## rpm tests NOT run (need root, passwordless sudo, or unshare -Urm)"
        RPM_TESTING_OFF_LOGGED=1
    fi

    return 0
}

# for test bodies: records one skipped test, which is what the report counts
skip_if_rpm_testing_off() {
    is_rpm_testing_off || return 1

    startSkipping
    fail # to count skipping tests
    endSkipping
    return 0
}

# for helpers that run more than once per test -- shunit2 counts every fail() as one skipped
# test, so count only the first call of a given test. shunit2 exports SU_FUNC and rewrites
# it for every test it runs. Mark the test only when we really skip, or a helper called
# twice would skip the second call with rpm available.
skip_rpm_testing_off_once() {
    is_rpm_testing_off || return 1

    if [ -n "$SU_FUNC" ] && [ "$RPM_TESTING_OFF_COUNTED" = "$SU_FUNC" ]; then
        return 0
    fi

    RPM_TESTING_OFF_COUNTED="$SU_FUNC"
    skip_if_rpm_testing_off
}

repo_up() {
    sudo_up

    POLDEK_TESTING_DENIED_FILES=""
    [ -n "$REPO" ] && $SUDO rm -rf $REPO/packages.*
    [ -n "$TMPDIR" ] && $SUDO rm -rf $TMPDIR/*.*
}

tearDown() {
    repo_up
}

setUp() {
    repo_up
}

abort() {
    echo "$1, ABORT"
    exit 1
}

ensure_commands() {
    while [ $# -gt 0 ]; do
        local cmd=$1
        shift
        command -v $cmd 2>&1 >/dev/null || abort "$cmd is missing"
    done
}

build_repo_packages() {
    local n=$(find $SOURCE_REPO/ -name \*.rpm | wc -l)
    if [ $n -lt 20 ]; then
        for i in a b c d e f g h i j k l m n o p r s t u w x y v z; do
            rpm="$i-1-1.noarch.rpm"
            [ -f "$SOURCE_REPO/$rpm" ] && continue
            #msgn "./build-test-rpm.sh -d $SOURCE_REPO/ -n $i -f /bin/$i -f /usr/share/doc/$i/README -v 1"
            ./build-test-rpm.sh -d $SOURCE_REPO/ -n $i -f /bin/$i -f /usr/share/doc/$i/README -v 1  >/dev/null 2>&1 || abort "build package $i failed"
            ./build-test-rpm.sh -d $SOURCE_REPO/ -n "$i$i" -f /bin/$i$i -f /usr/share/doc/$i$i/README -v 1  >/dev/null 2>&1 || abort "build package $i$i failed"
            ./build-test-rpm.sh -d $SOURCE_REPO/ -n "$i$i$i" -f /bin/$i$i$i -f /usr/share/doc/$i$i$i/README -v 1  >/dev/null 2>&1 || abort "build package $i$i$i failed"
        done
    fi
    local i=sh
    ./build-test-rpm.sh -d $SOURCE_REPO/ -n $i -f /bin/$i -f /usr/share/doc/$i/README -v 1  >/dev/null 2>&1 || abort "build package $i failed"
}

build_package() {
    destdir=$1; shift
    name=$1; shift
    version=""

    [ $# -gt 0 ] && version="$1";
    #echo "v $version"
    if [ -n "$version" ]; then
        if echo "$version" | grep -E -qv -- '^-'; then
            shift
        else
            version="1-1"
        fi
    fi
    [ -n "$version" ] || version="1-1"

    params=""
    [ $# -gt 0 ] && params="$@";

    local log=$TMPDIR/$name.build.log

    eval ./build-test-rpm.sh -d $destdir -n $name -v $version "$params" >$log 2>&1
    if [ $? -ne 0 ]; then
        fail "build package '$name' failed"
        exit
    fi

    if [ "$VERBOSE" = "vv" ]; then
        echo "./build-test-rpm.sh -d $destdir -n $name -v $version $params"
        cat $log
    fi

    BUILT_PACKAGE_NVR="$name-$version"
}

oneTimeSetUp() {
    sudo_up
    ensure_commands "grep" "zgrep" "zstdgrep" "perl" "fakeroot" "rpm" "rpmbuild" "timeout"

    [ ! -d "$PWD/sh/lib" ] && abort "$PWD is not a tests directory (missing sh/lib)"

    SHLIBDIR="$PWD/sh/lib"

    SOURCE_REPO=${SOURCE_REPO:-"${PWD}/repo"}
    mkdir -p $SOURCE_REPO
    [ ! -d "$SOURCE_REPO" ] && abort "$SOURCE_REPO: no such directory"

    TMP=${TMP:-""}
    TMPDIR=${TMPDIR:-""}
    [ -z "$TMP" ] && TMP="${TMPDIR}"
    [ -z "$TMP" ] && TMP="/tmp"
    TMP="${TMP}/poldek-tests"
    TMPDIR="$TMP"

    export TMP TMPDIR

    $SUDO rm -rf $TMPDIR
    mkdir -p $TMPDIR
    [ ! -d $TMPDIR ] && abort "$TMPDIR: no such directory"


    CACHEDIR="$TMPDIR/kesz"
    rm -rf $CACHEDIR
    mkdir -p $CACHEDIR
    [ ! -d $CACHEDIR ] && abort "$CACHEDIR: no such directory"

    REPO="$TMPDIR/repo"
    REPOURL="test://$TMPDIR/repo/"
    REPONAME="testrepo"

    mkdir -p $REPO
    [ ! -d $REPO ] && abort "mkdir $REPO failed?"

    PATH="$PATH:$PWD/sh/lib/"

    POLDEK="$PWD/../cli/poldek"
    $POLDEK --version >/dev/null; # produce .libs/lt-poldek

    POLDEK="$PWD/../cli/.libs/lt-poldek"
    POLDEK_NOCONF="$POLDEK --noconf -Ovfile_retries=1 --cachedir $CACHEDIR"
    POLDEK_UP="$POLDEK --conf ./sh/lib/vftest-fetch.conf --cachedir $CACHEDIR"

    POLDEK_RAW="$PWD/../cli/.libs/lt-poldek"
    POLDEK_RAW_UP="$POLDEK --conf ./sh/lib/vftest-fetch.conf --cachedir $CACHEDIR"

    build_repo_packages
    ln -sf $SOURCE_REPO/*.rpm $REPO/ || abort "make symlinks failed"
    tearDown
}

poldek_up() {
    $POLDEK_UP -Osource="$REPONAME,type=pndir $REPOURL" $@
}

poldek_quiet_up() {
    local cmd=$(echo $POLDEK_UP | sed 's|\-v|-q|g');
    $cmd -Osource="$REPONAME,type=pndir $REPOURL" $@
}

oneTimeTearDown() {
   if echo $TMPDIR | grep -q poldekTest; then rm -rf $TMPDIR; fi
   tmpdir=$(dirname $TMPDIR)
   TMP="$tmpdir"
   TMPDIR="$tmpdir"
}

die_if_empty_index() {
    die_if_invalid_index $1 ""
}

die_if_invalid_index() {
    local anindex=$1
    local expected_packages=$2

    GREP="zgrep"
    CAT="zcat"
    if echo $anindex | grep -qP '.(ndir|toc)$'; then
        GREP="grep"
        CAT="cat"
    elif echo $anindex | grep -qP '.gz$'; then
        GREP="zgrep"
        CAT="zcat"
    elif echo $anindex | grep -qP '.zst$'; then
        GREP="zstdgrep"
        CAT="zstdcat"
    fi

    if [ ! -f $anindex ]; then
        fail "$anindex not created"
        return
    fi

    if echo $anindex | grep -q "diff.toc"; then
        local expected_lines="$expected_packages"

        local lines=$($CAT $anindex | wc -l)
        if [ "$lines" != "$expected_lines" ]; then
            fail "$anindex contains $lines lines, expected $expected_lines"
        fi
        return
    fi

    anindex_type="pdir"
    if echo $anindex | grep -q \.ndir\.; then anindex_type="pndir"; fi
    if echo $anindex | grep -qP \.ndir$; then anindex_type="pndir"; fi
    anindex_real_type="pdir"

    if $GREP -q '^tndb[0-9]\.[0-9]' $anindex; then
        anindex_real_type="pndir"
    fi

    if [ "$anindex_real_type" != "$anindex_type" ]; then
        fail "invalid $anindex type $anindex_type, real is $anindex_real_type"
    fi

    if $GREP -q 'Contains 0 packages' $anindex; then
        if [ "$expected_packages" != "0" ]; then
            fail "empty $anindex"
        fi
    fi

    if [ -n "$expected_packages" ]; then
       if ! $GREP -q "Contains $expected_packages packages" $anindex; then
            fail "$anindex: number of packages mismatch"
       fi
    fi
}

check_repo_content() {
    local repo=$1
    local limit=${2:-"10"}

    if [ -z "$repo" ]; then
        fail "check_repo_content(): missing repo arg"
        return 1
    fi

    msgn "check_repo_content $repo limit=$limit"
    msg "$POLDEK_NOCONF -v -s $repo --skip-installed --cmd \"ls --qf '%{NAME}\n'\""

    local packages=$($POLDEK_NOCONF -q -s $repo --skip-installed ls --qf '%{NAME}\n');
    msg "$POLDEK_NOCONF -v -s $repo --skip-installed --cmd 'desc -L *'"
    $POLDEK_NOCONF -q --skip-installed -s $repo --cmd 'desc -L *' > $TMPDIR/changelogs

    local n=0
    for p in $packages; do
        n=$(expr $n + 1)
        [ $n -gt $limit ] && break

        #msg "package $p"

        # check changelog
        grep -qP "^\- $p package changelog entry" $TMPDIR/changelogs
        if [ $? -ne 0 ]; then
          msgn "$POLDEK_UP -q --skip-installed -s $repo --cmd 'desc -L $p'"
          fail "$p: missing changelog"
          break
        fi

        # check files
        msg "$POLDEK_NOCONF -q --skip-installed -s $repo desc -ll $p"
        local files=$($POLDEK_NOCONF -q --skip-installed -s $repo desc -ll $p | grep '/' | cut -f2)
        #msg "pkg $name, files: $files"
        echo $files | grep -q "/bin/$p"; [ $? -eq 0 ] || fail "/bin/$p: no file"
        echo $files | grep -q "/usr/share/doc/$p/README"; [ $? -eq 0 ] || fail "/usr/share/doc/$p/README: no file"
    done
    [ $n -eq 0 ] && fail "$repo empty repo?"
    return 0;
}


add_package_to_repo() {
    for p in $(ls $SOURCE_REPO/*.rpm); do
        [ ! -f $p ] && fail "no packages in $SOURCE_REPO?"
        [ ! -f $p ] && break
        package=$(basename $p)

        if [ ! -f "$REPO/$package" ]; then
            ln -sf $p $REPO/$package
            msg "added $package"
            return 0
        fi
    done
}


remove_package_from_repo() {
    package=$(find $REPO -name \*.rpm | head -1)
    rm -f $package
    msg "removed $(basename $package)"
}

random_package() {
    perl -e '@s=(a..z); print $s[rand(@s)]'
}

random_change_repo() {
    to_add=$(random_package)
    to_del=$(random_package)

    while [ -f $REPO/$to_add-*.rpm ]; do
        to_add=$(random_package)
    done

    if [ ! -f $REPO/$to_del-*.rpm ]; then
        to_del=$(random_package)
    fi

    [ "$to_add" == "$to_del" ] && to_del="none"
    msg "adding $to_add, removing $to_del";

    nadded=0
    nremoved=0
    for i in $SOURCE_REPO/${to_add}*.rpm; do
        bn=$(basename $i);

        [ ! -f $i ] && continue;
        [ -f $REPO/$bn ] && continue;

        nadded=$(expr $nadded + 1)
        msg "    - add $bn"
        ln -sf $i $REPO/$bn
    done

    for i in $REPO/${to_del}*.rpm; do
        bn=$(basename $i);

        [ ! -f $i ] && continue
        msg "   - rm $bn"
        rm -f $i
        nremoved=$(expr $nremoved + 1)
    done
    nchanges=$(expr $nadded + $nremoved)
    [ "$nchanges" = "0" ] && random_change_repo

    # avoid empty repo
    n=$(ls $REPO/*.rpm | wc -l)
    [ "$n" = "0" ] && random_change_repo

    #msg "Added $nadded and $nremoved removed"
}
