#!/bin/bash

set -e
[ "$DEBUG" ] && set -x

USAGE="USAGE: $(basename "$0") [run|edit] <docker-compose-service>"
GOSS_FILES_PATH="${GOSS_FILES_PATH:-.}"

info() {
    echo -e "INFO: $*" >&2;
}
error() {
    echo -e "ERROR: $*" >&2;
    [[ -e "$service" ]] && docker logs "$service"
    exit 1;
}

cleanup() {
    set +e
    { kill "$log_pid" && wait "$log_pid"; } 2> /dev/null
    [ "$DEBUG" ] || rm -rf "$tmp_dir"
    if [[ -n "$service" ]]; then
        info "Stopping container"
        docker-compose stop > /dev/null
    fi
}

run(){
    # Copy in goss
    cp "${GOSS_PATH}" "$tmp_dir/goss"
    chmod 755 "$tmp_dir/goss"
    [[ -e "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" ]] && install -m ugo+rw "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" "$tmp_dir"
    [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]] && install -m ugo+rw "${GOSS_FILES_PATH}/goss_wait.yaml" "$tmp_dir"
    [[ -n "${GOSS_VARS}" ]] && [[ -e "${GOSS_FILES_PATH}/${GOSS_VARS}" ]] && install -m ugo+rw "${GOSS_FILES_PATH}/${GOSS_VARS}" "$tmp_dir"

    # Switch between mount or cp files strategy
    GOSS_FILES_STRATEGY=${GOSS_FILES_STRATEGY:="mount"}
    case "$GOSS_FILES_STRATEGY" in
      mount)
        info "Starting docker container"
        docker-compose run -d -T --name "$service" -v "$tmp_dir:/goss" --rm "${@:2}"
        ;;
      cp)
        info "Creating docker container"
        docker-compose run -d -T --name "$service" --rm "${@:2}" > /dev/null
        info "Copy goss files into container"
        docker cp "$tmp_dir/." "$service:/goss"
        ;;
      *) error "Wrong goss files strategy used! Correct options are \"mount\" or \"cp\"."
    esac

    docker logs -f "$service" > "$tmp_dir/docker_output.log" 2>&1 &
    log_pid="$!"
    info "Container name: $service"
}

get_docker_file() {
    set +e
    if docker exec "$service" sh -c "test -e $1" > /dev/null; then
        mkdir -p "${GOSS_FILES_PATH}"
        info "Copied '$1' from container to '${GOSS_FILES_PATH}'"
        docker cp "$service:$1" "${GOSS_FILES_PATH}"
    fi
    set -e
}

# Main
tmp_dir=$(mktemp -d /tmp/tmp.XXXXXXXXXX)
chmod 777 "$tmp_dir"
# shellcheck disable=SC2154
trap 'ret=$?; cleanup; info "Test ran for a total of $SECONDS seconds"; exit $ret' EXIT
service="$2"

if [[ ! -f docker-compose.yaml && ! -f docker-compose.yml ]]; then
    echo "no docker-compose file found in ."
    exit 1
fi

state=$(docker inspect --format '{{.State.Status}}' "$service" 2> /dev/null || true)
if [[ "$state" == running ]]; then
    docker rm -f "$service" > /dev/null
elif [[ "$state" == exited ]]; then
    docker rm "$service" > /dev/null
fi

GOSS_PATH="${GOSS_PATH:-$(command -v goss 2> /dev/null || true)}"
[[ "$GOSS_PATH" ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; }
[[ "${GOSS_OPTS+x}" ]] || GOSS_OPTS="--color --format documentation"
[[ "${GOSS_WAIT_OPTS+x}" ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null"
GOSS_SLEEP="${GOSS_SLEEP:-0.2}"

case "$1" in
    run)
        run "$@"
        [[ "$GOSS_SLEEP" ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; }
        if [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]]; then
            info "Found goss_wait.yaml, waiting for it to pass before running tests"
            if [[ -z "${GOSS_VARS}" ]]; then
                if ! docker exec "$service" sh -c "/goss/goss -g /goss/goss_wait.yaml render | /goss/goss -g - validate $GOSS_WAIT_OPTS"; then
                    error "goss_wait.yaml never passed"
                fi
            else
                if ! docker exec "$service" sh -c "/goss/goss -g /goss/goss_wait.yaml --vars='/goss/${GOSS_VARS}' render | /goss/goss -g - validate $GOSS_WAIT_OPTS"; then
                    error "goss_wait.yaml never passed"
                fi
            fi
        fi
        info "Container health"
        if [ "true" != "$(docker inspect -f '{{.State.Running}}' $service)" ]; then
            error "The container failed to start"
        fi
        info "Running Tests"
        if [[ -z "${GOSS_VARS}" ]]; then
            docker exec "$service" sh -c "/goss/goss -g /goss/${GOSS_FILE:-goss.yaml} render | /goss/goss -g - validate $GOSS_OPTS"
        else
            docker exec "$service" sh -c "/goss/goss -g /goss/${GOSS_FILE:-goss.yaml} --vars='/goss/${GOSS_VARS}' render | /goss/goss -g - validate $GOSS_OPTS"
        fi
        ;;
    edit)
        run "$@"
        info "Run goss add/autoadd to add resources"
        docker exec -it "$service" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh'
        [[ -n "${GOSS_FILE}" ]] && get_docker_file "/goss/${GOSS_FILE}"
        get_docker_file "/goss/goss.yaml"
        get_docker_file "/goss/goss_wait.yaml"
        [[ -n "${GOSS_VARS}" ]] && get_docker_file "/goss/${GOSS_VARS}"
        ;;
    *)
        error "$USAGE"
esac
