#!/usr/bin/env bash

# credit to https://github.com/w0rp/ale for script ideas and the color vader
# output function.

# Say Hi
echo -en "Starting $(basename "$0") for VimWiki\n"
start_time=$(date +%s)

# For windows: Cmder bash is appending busybox to the path and
#   and a smlll vim is included, so that override the windows path vim
if [[ -v OLD_PATH ]]; then
    echo "Setting path from OLD_PATH : $OLD_PATH"
    export PATH="$OLD_PATH"
fi

printHelp() {
    cat << '        EOF' | sed -e 's/^        //'
        Usage: bash run_tests.sh [OPTIONS]

        Runs Vimwiki Vader tests or Vint in a Docker container

        -h (Help) Print help message

        -n (versioN) Specify vim/nvim version to run tests for.
           Specify "local" to run on your current vim install
           for example on Windows.
           Multiple versions can be specified by quoting the value and
           separating versions with a space. E.g. -n "vim1 vim2".
           Default is all available versions.

        -f (File) Space separated list of tests to run.
           E.g. -o "list_* z_success"

        -l (List) list available versions that can be used with the '-n' option

        -t (Type) Select test type: 'vader', 'vint', or 'all'

        -v (Verbose) Turn on verbose output.

        E.g. On Linux
        bash run_tests.sh -v -t vader -n "vim_7.4.1099 vim_8.1.0519" -f link_creation.vader issue_markdown.vader
        E.g. On Windows
        bash run_tests.sh -v -t vader -n local -f z_success.vader | cat
        EOF

    exit 0
}

printVersions() {
    # Print the names of all vim/nvim versions
    getVers
    exit 0
}

runVader() {
    # Run Vader tests
    echo -e "\nStarting Vader tests."
    local err=0

    # Parse tests files to execute
    if [[ -z $file_test ]]; then
        res="test/*"
    else
        read -ra TEST <<< "$file_test"
        for i in "${TEST[@]}"; do
            if [[ "$i" == *"*"* ]]; then
                res="$res test/${i}"
            elif [[ -f "$i" ]]; then
                res="$res test/${i}"
            elif [[ -f "${i}.vader" ]]; then
                res="$res test/${i}.vader"
            else
                printf "WARNING: Test \"%s\" not found.\n", "$i"
            fi
        done
    fi

    # Run tests for each specified version
    for v in $vers; do
        echo -e "\n\nRunning version: $v"
        echo -e "============================="

        # Set local environment variables
        if [[ "$v" == "local" ]]; then
            # Save HOME var
            home_save="$HOME"

            # Create temporary root
            mkdir -p "$tmp_dir/vader_wiki"
            mkdir -p "$tmp_dir/vader_wiki/home"
            mkdir -p "$tmp_dir/vader_wiki/home/test"
            mkdir -p "$tmp_dir/vader_wiki/testplugin"

            # Set vars
            export ROOT="$tmp_dir/vader_wiki/"
            export HOME="$tmp_dir/vader_wiki/home"
            vim="vim"
            vim_opt="-u ~/test/vimrc"
        else
            # Only set dockerized vars
            export ROOT="/"  # So no if in vimrc
            vim="/vim-build/bin/$v"
            vim_opt="-u test/vimrc"
        fi

        # Too talkative TODO make a verbose level 1..10 an 1 is not taking vim
        #if [[ "$verbose" != 0 ]]; then
        #    vim_opt+=' -V1'
        #fi
        # IDK why vim with -Es is returning ! and make fail:
        # -- tabnext profiling
        # -- map.vim
        vim_opt+=' -i NONE -Es '

        # set -o pipefail

        # Copy the resources to temporary directory
        if [[ "$v" == "local" ]]; then
            # flags=(--rm -v "$PWD/../:/testplugin" -v "$PWD/../test:/home" -w /testplugin vimwiki)
            echo -e "\nCopying resources to $ROOT"
            # Copy testplugin
            cp -rf "$wiki_path/"* "$ROOT/testplugin/"
            # Copy home
            cp -rf "$script_path/"* "$HOME/test/"
            # Copy rtp.vim
            cp -rf "$script_path/resources/rtp_local.vim" "$ROOT/rtp.vim"
            # Copy vader <- internet
            echo 'Cloning Vader (git, do not care the fatal)'
            git clone --depth 10 https://github.com/junegunn/vader.vim /tmp/vader_wiki/vader 2>&1
        fi

        # Run batch of tests
        # shellcheck disable=SC2086,SC2206
        if [[ "$res" != "" ]]; then
            if [[ "$v" == "local" ]]; then
                pushd "$tmp_dir/vader_wiki/testplugin" \
                    || echo 'Warning pushd testplugin failed'

                # Run the tests
                fcmd(){
                    $vim $vim_opt "+Vader! ${res}" 2>&1
                    return ${PIPESTATUS[1]}
                }
                echo -e "\nStarting Batch Vim/Vader:\n<- $res\n"
                type fcmd | sed  -n '/^    /{s/^    //p}' | sed '$s/.*/&;/' ; shift ;
                fcmd; ret=$?
                err=$(( err + ret ))
                echo -e "\nReturned Batch Vim/Vader -> $ret"

                popd \
                    || echo 'Warning popd also failed'
            else
                # In docker
                fcmd() {
                    docker run -a stderr -e "VADER_OUTPUT_FILE=/dev/stderr" \
                      "${flags[@]}" "$v" $vim_opt "+Vader! ${res}" 2>&1 \
                      | vader_filter | vader_color
                    return ${PIPESTATUS[1]}
                }
                echo -e "\nStarting Batch Vim/Vader:\n<- $res\n"
                type fcmd | sed  -n '/^    /{s/^    //p}' | sed '$s/.*/&;/' ; shift ;
                fcmd; ret=$?
                err=$(( err + ret ))
                echo -e "\nReturned Batch Docker/Vim/Vader -> $ret : ${PIPESTATUS[*]}"
            fi
        fi

        #set +o pipefail

        # Restore what must (I know it should be refactored in a while)
        if [[ "$v" == "local" ]]; then
            export HOME=$home_save
        fi
    done
    return $err
}

runVint() {
    local err=0
    cmd="vint -s . && vint -s test/vimrc"
    if echo "$vers" | grep "local" > /dev/null; then
        echo -e "\nRunning Vint: $cmd : in $wiki_path"
        pushd "$wiki_path" > /dev/null \
            || echo 'Warning pushd wiki_path failed'
        $cmd
        err=$(( err | $? ))
        popd > /dev/null \
            || echo 'Warning popd also failed'
    else
        echo -e "\nStarting Docker container and running Vint: $cmd"
        docker run -a stdout "${flags[@]}" bash -c "$cmd"
        err=$(( err | $? ))
    fi
    return $err
}

getVers() {
    # Get all possible version <- Dockerfile
    sed -n 's/.* -name \([^ ]*\) .*/\1/p' ../Dockerfile
}

vader_filter() {
    # Filter Vader Stdout
    local err=0
    # Keep indentation
    local IFS=''
    while read -r REPLY; do
        # Print only possible error cases
        if [[ "$REPLY" = *'docker:'* ]] || \
           [[ "$REPLY" = *'Starting Vader:'* ]] || \
           [[ "$REPLY" = *'Vader error:'* ]] || \
           [[ "$REPLY" = *'Vim: Error '* ]]; then
            echo "$REPLY"
        elif [[ "$REPLY" = *'[EXECUTE] (X)'* ]] || \
            [[ "$REPLY" = *'[ EXPECT] (X)'* ]]; then
            echo "$REPLY"
            err=1
        elif [[ "$REPLY" = *'Success/Total:'* ]]; then
            success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)"
            total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)"
            if [ "$success" -lt "$total" ]; then
                err=1
            fi
            echo "$REPLY"
        elif [[ "$verbose" != 0 ]]; then
            # just print everything
            echo "$REPLY"
        fi
    done

    if [[ "$err" == 1 ]]; then
        echo -e "\033[0;31m"
        echo -e "!---------Failed tests detected---------!"
        echo -e "Run with the '-v' flag for verbose output"
        echo -e "\033[0m"
    fi
    return $err
}


red='\033[0;31m'
green='\033[0;32m'
nc='\033[0m'
vader_color() {
    while read -r; do
        if [[ "$REPLY" = *'[EXECUTE] (X)'* ]] || \
            [[ "$REPLY" = *'[ EXPECT] (X)'* ]] || \
            [[ "$REPLY" = *'Vim: Error '* ]] || \
            [[ "$REPLY" = *'Vader error:'* ]]; then
            echo -en "$red"
        elif [[ "$REPLY" = *'[EXECUTE]'* ]] || [[ "$REPLY" = *'[  GIVEN]'* ]]; then
            echo -en "$nc"
        fi

        if [[ "$REPLY" = *'Success/Total'* ]]; then
            success="$(echo -n "$REPLY" | grep -o '[0-9]\+/' | head -n1 | cut -d/ -f1)"
            total="$(echo -n "$REPLY" | grep -o '/[0-9]\+' | head -n1 | cut -d/ -f2)"

            if [ "$success" -lt "$total" ]; then
                echo -en "$red"
            else
                echo -en "$green"
            fi

            echo "$REPLY"
            echo -en "$nc"
        else
            echo "$REPLY"
        fi
    done

    echo -en "$nc"
}

# path of the script, supposing no spaces
script_file="$(dirname "$0")"
script_path="$( realpath "$script_file" )"
wiki_path="$( realpath "$script_path/.." )"
tmp_dir="$(dirname "$(mktemp -u)")"

# list of vim/nvim versions
vers="$(getVers)"

# type of tests to run - vader/vint/all
type="all"

# verbose output flag
verbose=0

# only run these tests
file_test=""

# docker flags
flags=(--rm -v "$PWD/../:/testplugin" -v "$PWD/../test:/home" -w /testplugin vimwiki)

while getopts ":hvn:lt:f:" opt; do
    case ${opt} in
        h )
            printHelp
            ;;
        n )
            vers="$OPTARG"
            ;;
        v )
            verbose=1
            ;;
        l )
            printVersions
            ;;
        t )
            type="$OPTARG"
            ;;
        f )
            file_test="$OPTARG"
            ;;
        \? )
            echo "Invalid option: $OPTARG" 1>&2
            exit 1
            ;;
        : )
            echo "Invalid option: $OPTARG requires an argument" 1>&2
            exit 1
            ;;
    esac
done

# shift out processed parameters
shift $((OPTIND -1))

# error handling for non-option arguments
if [[ $# -ne 0 ]]; then
    echo "Error: Got $# non-option arguments." 1>&2
    exit 1
fi

# stop tests on ctrl-c or ctrl-z
trap exit 1 SIGINT SIGTERM

# Global error return of the script
o_error=0

# Select which tests should run
case $type in
    "vader" )
        runVader ; err=$?
        echo "Main Vader: returned $err"
        o_error=$(( err | o_error ))
        ;;
    "vint" )
        runVint ; err=$?
        echo "Main Vint: returned $err"
        o_error=$(( err | o_error ))
        ;;
    "all" )
        runVint ; err=$?
        echo "Main Vint: returned $err"
        o_error=$(( err | o_error ))
        runVader ; err=$?
        echo "Main Vader: returned $err"
        o_error=$(( err | o_error ))
        ;;
    * )
        echo "Error: invalid type - '$type'" 1>&2
        exit 1
esac

# Calcultate time
end_time=$(date +%s)
sec_time=$((end_time - start_time))
printf -v script_time '%dh:%dm:%ds' $((sec_time/3600)) $((sec_time%3600/60)) $((sec_time%60))

# Exit
echo -ne "Script $(basename "$0"), in $script_time, Returned -> $o_error\n\n"
exit $o_error
