#!/usr/bin/env bash

# Podman and Toolbox commands to run
PODMAN=${PODMAN:-podman}
TOOLBOX=${TOOLBOX:-toolbox}

# Helpful globals
LATEST_FEDORA_VERSION=${LATEST_FEDORA_VERSION:-"32"}
DEFAULT_FEDORA_VERSION=${DEFAULT_FEDORA_VERSION:-"f31"}
REGISTRY_URL=${REGISTRY_URL:-"registry.fedoraproject.org"}
TOOLBOX_DEFAULT_IMAGE=${TOOLBOX_DEFAULT_IMAGE:-"registry.fedoraproject.org/f31/fedora-toolbox:31"}
TOOLBOX_TIMEOUT=${TOOLBOX_TIMEOUT:-100}
PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-100}

# Colors
LGC='\033[1;32m' # Light Green Color
LBC='\033[1;34m' # Light Blue Color
NC='\033[0m' # No Color


################
#  run_podman  #  Invoke $PODMAN, with timeout, using BATS 'run'
################
#
# This is the preferred mechanism for invoking podman: first, it
# invokes $PODMAN, which may be 'podman-remote' or '/some/path/podman'.
#
# Second, we log the command run and its output. This doesn't normally
# appear in BATS output, but it will if there's an error.
#
# Next, we check exit status. Since the normal desired code is 0,
# that's the default; but the first argument can override:
#
#     run_podman 125  nonexistent-subcommand
#     run_podman '?'  some-other-command       # let our caller check status
#
# Since we use the BATS 'run' mechanism, $output and $status will be
# defined for our caller.
#
function run_podman() {
    # Number as first argument = expected exit code; default 0
    expected_rc=0
    case "$1" in
        [0-9])           expected_rc=$1; shift;;
        [1-9][0-9])      expected_rc=$1; shift;;
        [12][0-9][0-9])  expected_rc=$1; shift;;
        '?')             expected_rc=  ; shift;;  # ignore exit code
    esac

    # stdout is only emitted upon error; this echo is to help a debugger
    echo "\$ $PODMAN $*"
    run timeout --foreground -v --kill=10 $PODMAN_TIMEOUT $PODMAN "$@" 3>/dev/null
    # without "quotes", multiple lines are glommed together into one
    if [ -n "$output" ]; then
        echo "$output"
    fi
    if [ "$status" -ne 0 ]; then
        echo -n "[ rc=$status ";
        if [ -n "$expected_rc" ]; then
            if [ "$status" -eq "$expected_rc" ]; then
                echo -n "(expected) ";
            else
                echo -n "(** EXPECTED $expected_rc **) ";
            fi
        fi
        echo "]"
    fi

    if [ -n "$expected_rc" ]; then
        if [ "$status" -ne "$expected_rc" ]; then
            die "exit code is $status; expected $expected_rc"
        fi
    fi
}

function run_toolbox() {
    # Number as first argument = expected exit code; default 0
    expected_rc=0
    case "$1" in
        [0-9])           expected_rc=$1; shift;;
        [1-9][0-9])      expected_rc=$1; shift;;
        [12][0-9][0-9])  expected_rc=$1; shift;;
        '?')             expected_rc=  ; shift;;  # ignore exit code
    esac

    # stdout is only emitted upon error; this echo is to help a debugger
    echo "\$ $TOOLBOX $*"
    run timeout --foreground -v --kill=10 $TOOLBOX_TIMEOUT $TOOLBOX "$@" 3>/dev/null
    # without "quotes", multiple lines are glommed together into one
    if [ -n "$output" ]; then
        echo "$output"
    fi
    if [ "$status" -ne 0 ]; then
        echo -n "[ rc=$status ";
        if [ -n "$expected_rc" ]; then
            if [ "$status" -eq "$expected_rc" ]; then
                echo -n "(expected) ";
            else
                echo -n "(** EXPECTED $expected_rc **) ";
            fi
        fi
        echo "]"
    fi

    if [ -n "$expected_rc" ]; then
        if [ "$status" -ne "$expected_rc" ]; then
            die "exit code is $status; expected $expected_rc"
        fi
    fi
}

# Toolbox helper functions

function is_toolbox_ready() {
    toolbox_container="$1"
    expected_string="Listening to file system and ticker events"
    num_of_tries=5
    timeout=2

    run_podman logs $toolbox_container

    for ((i = 0; i < $num_of_tries; i++)); do
        if [[ "$output" =~ .*"$expected_string".* ]]; then
            return
        fi

        sleep $timeout
        run_podman logs $toolbox_container
    done

    echo "Output of 'podman logs $toolbox_container':"
    echo "$output"
    echo ""

    die "container $toolbox_container was not ready in time"
}

# Functions to prepare environment


function create_toolbox() {
    echo "# [create_toolbox]"

    local numberof="$1"
    local naming="$2"
    local image="$3"

    if [ "$numberof" = "" ]; then
        numberof=${numberof:-1}
    fi

    if [ "$image" = "" ]; then
        image=$TOOLBOX_DEFAULT_IMAGE
    fi

    for i in $(seq "$numberof"); do
        if [ "$naming" = "" ]; then
            run_toolbox '?' -y create -i "$image"
        else
            run_toolbox '?' -y create -c "$naming-$i" -i "$image"
        fi
    done
}


function get_images() {
    echo "# [get_images]"

    local numberof="$1"
    local image=""

    if [ "$numberof" = "" ]; then
        numberof=${numberof:-1}
    fi

    for i in $(seq $numberof); do
        local version=$[$LATEST_FEDORA_VERSION-$i]
        image="$REGISTRY_URL/f$version/fedora-toolbox:$version"
        run_podman pull "$image" || echo "Podman couldn't pull the image."
    done
}


function remove_all_images() {
    echo "# [remove_all_images]"
    run_podman images --all --format '{{.Repository}}:{{.Tag}} {{.ID}}'
    for line in "${lines[@]}"; do
        set $line
        run_podman rmi --force "$1" >/dev/null 2>&1 || true
        run_podman rmi --force "$2" >/dev/null 2>&1 || true
    done
}

function remove_all_images_but_default() {
    echo "# [remove_all_images_but_default]"
    found_needed_image=1
    run_podman images --all --format '{{.Repository}}:{{.Tag}} {{.ID}}'
    for line in "${lines[@]}"; do
        set $line
        if [ "$1" == "$TOOLBOX_DEFAULT_IMAGE" ]; then
            found_needed_image=1
        else
            run_podman rmi --force "$1" >/dev/null 2>&1 || true
        fi
    done
}


function get_image_name() {
    echo "# [get_image_name]"
    local type="$1"
    local version="$2"

    if [ -z "$type" ]; then
        type=${type:-fedora}
    fi

    if [ -z "$version" ]; then
        version=${version:-$DEFAULT_FEDORA_VERSION}
    fi

    case "$type" in
        fedora)
            echo "$REGISTRY_URL/f$version/fedora-toolbox:$version"
            ;;
    esac
}

function remove_all_containers() {
    echo "# [remove_all_containers]"
    run_toolbox '?' rm --all --force
}


# BATS specific functions

#########
#  die  #  Abort with helpful message
#########
function die() {
    echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"  >&2
    echo "#| FAIL: $*"                                           >&2
    echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
    false
}


########
#  is  #  Compare actual vs expected string; fail w/diagnostic if mismatch
########
#
# Compares given string against expectations, using 'expr' to allow patterns.
#
# Examples:failed to inspect
#
#   is "$actual" "$expected" "descriptive test name"
#   is "apple" "orange"  "name of a test that will fail in most universes"
#   is "apple" "[a-z]\+" "this time it should pass"
#
function is() {
    local actual="$1"
    local expect="$2"
    local testname="${3:-FIXME}"

    if [ -z "$expect" ]; then
        if [ -z "$actual" ]; then
            return
        fi
        expect='[no output]'
    elif expr "$actual" : "$expect" >/dev/null; then
        return
    fi

    # This is a multi-line message, which may in turn contain multi-line
    # output, so let's format it ourself, readably
    local -a actual_split
    readarray -t actual_split <<<"$actual"
    printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
    printf "#|     FAIL: $testname\n"                          >&2
    printf "#| expected: '%s'\n" "$expect"                     >&2
    printf "#|   actual: '%s'\n" "${actual_split[0]}"          >&2
    local line
    for line in "${actual_split[@]:1}"; do
        printf "#|         > '%s'\n" "$line"                   >&2
    done
    printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2
    false
}

#################
#  parse_table  #  Split a table on '|' delimiters; return space-separated
#################
#
# See sample .bats scripts for examples. The idea is to list a set of
# tests in a table, then use simple logic to iterate over each test.
# Columns are separated using '|' (pipe character) because sometimes
# we need spaces in our fields.
#
function parse_table() {
    while read line; do
        test -z "$line" && continue

        declare -a row=()
        while read col; do
            dprint "col=<<$col>>"
            row+=("$col")
        done <  <(echo "$line" | tr '|' '\012' | sed -e 's/^ *//' -e 's/\\/\\\\/g')

        printf "%q " "${row[@]}"
        printf "\n"
    done <<<"$1"
}
