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

SCRIPTPATH="$(cd "$(dirname "$0")"; pwd -P)"
BUILD_DIR="${SCRIPTPATH}/../../build"
TEST_FAILURES_LOG="${BUILD_DIR}/test-failures.log"

AEIS="${BUILD_DIR}/ec2-instance-selector"
GLOBAL_PARAMS="--max-results=40"

## Print to stderr
function echoerr() {
    echo "$@" 1>&2;
}

## Sort a bash array
function sort_array() {
    local input=( "$@" )
    IFS=$'\n' 
    local sorted=($(sort <<<"${input[*]}"))
    unset IFS
    echo "${sorted[*]}"
}

## Checks if expected items (consumed as an array in args) 
## are all contained in the actual list (consumed on stdin)
function assert_contains_instance_types() {
    local expected=( "$@" )
    local actual=()
    while read actual_input; do
        actual+=($actual_input)
    done
    [[ 0 -eq "${#actual[@]}" ]] && return 1
    local actual_sorted=($(sort_array "${actual[@]}"))
    local expected_sorted=($(sort_array "${expected[@]}"))
    
    for expectation in "${expected_sorted[@]}"; do
        if [[ ! " ${actual_sorted[*]} " =~ ${expectation} ]]; then
            echoerr -e "\t🔺ACTUAL: ${actual_sorted[*]}\n\t🔺Expected: ${expected_sorted[*]}"
            return 1
        fi
    done
}

## Executes an expected vs actual test execution of amazon-ec2-instance-selector
## Test success or failure is output to stdout and failures are also logged to a file ($TEST_FAILURES_LOG)
##        $1        = test name string
##        shift; $@ = params to amazon-ec2-instance-selector (i.e. --vcpus=2)
##        STDIN     = expected list
function execute_test() {
    local test_name=$1
    shift
    local params=( "$@" )
    local expected=()
    while read expected_input; do
        expected+=($expected_input)
    done
    [[ 0 -eq "${#expected[@]}" ]] && return 1

    echo "=========================== Test: ${test_name} ==========================="

    for p in "${params[@]}"; do
        if $AEIS ${p} ${GLOBAL_PARAMS} | assert_contains_instance_types "${expected[*]}"; then  
            echo "✅ ${test_name} \"$p\" passed!"
        else
            echo "❌ Failed ${test_name} \"$p\"" | tee "${TEST_FAILURES_LOG}"
        fi
    done   
    echo -e "========================= End Test: ${test_name} ===========================\n\n" 
}

## Clean up previous test failures
rm -f "${TEST_FAILURES_LOG}"

################################################ TESTS ################################################

expected=(t3a.micro t3.micro t2.micro)
params=(
    "--memory=1"
    "--memory=1GiB"
    "--memory=1 GiB"
    "--memory=1gb"
    "--memory=1.0"
    "--memory-min=1 --memory-max=1"
    "--memory=1024m"
)
echo "${expected[*]}" | execute_test "Memory 1 GiB" "${params[@]}"

expected=(i3en.6xlarge inf1.6xlarge z1d.6xlarge)
params=(
    "--vcpus=24"
    "--vcpus-min=24 --vcpus-max=24"
)
echo "${expected[*]}" | execute_test "24 VCPUs" "${params[@]}"

expected=(g2.8xlarge g3.16xlarge g4dn.12xlarge p3.8xlarge)
params=(
    "--gpus=4"
    "--gpus-min=4 --gpus-max=4"
)
echo "${expected[*]}" | execute_test "4 GPUs" "${params[@]}"


expected=(p2.16xlarge)
params=(
    "--vcpus-to-memory-ratio=1:12"
)
echo "${expected[*]}" | execute_test "1:12 vcpus-to-memory-ratio" "${params[@]}"


expected=(p2.8xlarge)
params=(
    "--gpu-memory-total=96"
    "--gpu-memory-total=96gb"
    "--gpu-memory-total=96GiB"
    "--gpu-memory-total=98304m"
    "--gpu-memory-total-min=96 --gpu-memory-total-max=96"
)
echo "${expected[*]}" | execute_test "96 GiB gpu-memory-total" "${params[@]}"


expected=(a1.large c3.large c4.large c5.large c5a.large c5d.large c5n.large c6g.large c6gd.large \
    g4dn.2xlarge g4dn.4xlarge g4dn.xlarge i3.large i3en.large m1.large m3.large m5.large m5a.large m5ad.large m5d.large)
params=(
    "--network-interfaces=3"
    "--network-interfaces-min=3 --network-interfaces-max=3"
    "--network-interfaces 3 --memory-min=1 --vcpus-min=1"
)
echo "${expected[*]}" | execute_test "3 network interfaces" "${params[@]}"


expected=(c5n.18xlarge c5n.metal g4dn.metal i3en.24xlarge i3en.metal inf1.24xlarge m5dn.24xlarge m5n.24xlarge p3dn.24xlarge r5dn.24xlarge r5n.24xlarge)
params=(
    "--network-performance=100"
    "--network-performance-min=100 --network-performance-max=100"
    "--network-performance=100 --vcpus-min 1 --memory-min=1"
)
echo "${expected[*]}" | execute_test "100 Gib/s Networking Performance" "${params[@]}"

expected=(t3.micro)
params=(
    "--allow-list=^t3\.micro$"
    "--allow-list=t3.micro"
    "--allow-list=t[03].mic"
    "--allow-list=t3.mi"
)
echo "${expected[*]}" | execute_test "Allow List" "${params[@]}"

expected=(t1.micro t2.micro t3.micro t3a.micro)
params=(
    "--deny-list=^[a-z].*\.[0-9]*(sm|me|la|na|xl).*"
    "--deny-list=^[a-z].*\.[0-9]*(sm|me|la|na|xl).* --allow-list=t.*"
)
echo "${expected[*]}" | execute_test "Deny List" "${params[@]}"


expected=(t2.micro t2.nano t2.small)
params=(
    "--burst-support --vcpus-max=1"
    "--burst-support --vcpus-max=1 --hypervisor=xen"
    "--burst-support --vcpus-max=1 --hibernation-support"
    "--burst-support --vcpus-max=1 --usage-class=on-demand"
)
echo "${expected[*]}" | execute_test "Burst Support" "${params[@]}"


expected=(f1.16xlarge f1.2xlarge f1.4xlarge)
params=(
    "--fpga-support"
    "--fpga-support --hypervisor=xen"
    "--fpga-support --cpu-architecture=x86_64"
    "--fpga-support --vcpus-min 1"
)
echo "${expected[*]}" | execute_test "FPGAs" "${params[@]}"



if [[ -f "${TEST_FAILURES_LOG}" ]]; then
    echo -e "\n\n\n=========================== FAILURE SUMMARY ===========================\n"
    cat "${TEST_FAILURES_LOG}"
    echo -e "\n========================= END FAILURE SUMMARY ========================="
    exit 1
fi
