#!/bin/bash

PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

USAGE="
    ${0} -a <action> [-e] [-V] -r <role-id> -s <secret-id> -n <key_name> -f <key_field> -P <profile_name> -p <aws_profile_name> -u <uri>
"

(return 0 2>/dev/null) && THALLIUM_SOURCED=1 || THALLIUM_SOURCED=0

UUIDGEN_BIN="${UUIDGEN_BIN:-$(which uuidgen||true)}"
export THALLIUM_UUID=""

if [ -e "${UUIDGEN_BIN}" ];
then
    export THALLIUM_UUID="$("${UUIDGEN_BIN}")"
fi

VAULT_BIN="${VAULT_BIN:-$(which vault||true)}"
ENV_BIN="${ENV_BIN:-$(which env||true)}"
JSON_DOTENV_BIN="${JSON_DOTENV_BIN:-$(which json-dotenv||true)}"

export THALLIUM_DATE="$(date '+%Y-%m-%d')"
export THALLIUM_DATETIME="$(date '+%Y-%m-%d %H:%M:%S')"
export THALLIUM_DATE_NUM="$(date '+%Y%m%d')"
export THALLIUM_DATETIME_NUM="$(date '+%Y%m%d%H%M%S')"
export THALLIUM_DATENANO="$(date '+%N')"
export THALLIUM_TIMESTAMP="$(date '+%s')"

THALLIUM_CONF_FILE="${THALLIUM_CONF_FILE:-"${HOME}/.config/fd-thallium"}"

if [ ! -f "${THALLIUM_CONF_FILE}" ];
then
    THALLIUM_CONF_FILE="${HOME}/.config/fd-thallium.json"
fi

THALLIUM_KEY_FIELDS=()

while getopts 'hetVa:f:r:s:n:o:p:P:u:' OPT;
do
    case ${OPT} in
    h)
        echo ${USAGE}
        exit 0
        ;;
    a)
        THALLIUM_ACTION="${OPTARG}"
        ;;
    e)
        THALLIUM_SET_ENV="env"
        ;;
    f)
        THALLIUM_KEY_FIELDS+=("${OPTARG}")
        ;;
    r)
        THALLIUM_ROLE_ID="${OPTARG}"
        ;;
    s)
        THALLIUM_SECRET_ID="${OPTARG}"
        ;;
    t)
        THALLIUM_EXPORT_TOKEN="true"
        ;;
    n)
        THALLIUM_KEY_NAME="${OPTARG}"
        ;;
    o)
        THALLIUM_OUTENV_FORMAT="${OPTARG}"
        ;;
    p)
        THALLIUM_AWS_PROFILE_NAME="${OPTARG}"
        ;;
    P)
        THALLIUM_PROFILE_NAME="${OPTARG}"
        ;;
    u)
        THALLIUM_URI="${OPTARG}"
        ;;
    V)
        THALLIUM_EXPORT_VAULT="true"
        ;;
    :)
        echo "Option -${OPTARG} requires an argument." >&2
        exit 1
        ;;
    esac
done

THALLIUM_PROFILE_NAME="${THALLIUM_PROFILE_NAME:-default}"

WRAPPER_OPT=()

if [[ "${@:${OPTIND}-1:1}" == "--" ]];
then
    for OPT in "${@:${OPTIND}}";
    do
        WRAPPER_OPT+=("${OPT}")
    done;
fi

if [ -z "${THALLIUM_ROLE_ID}" ] && [ -f "${THALLIUM_CONF_FILE}" ];
then
    if [[ "${THALLIUM_CONF_FILE}" == *.json ]];
    then
        THALLIUM_ROLE_ID="$(jq -r '.["'"${THALLIUM_PROFILE_NAME}"'"].role_id // empty' "${THALLIUM_CONF_FILE}")"
        THALLIUM_SECRET_ID="$(jq -r '.["'"${THALLIUM_PROFILE_NAME}"'"].secret_id // empty' "${THALLIUM_CONF_FILE}")"
        THALLIUM_URI="$(jq -r '.["'"${THALLIUM_PROFILE_NAME}"'"].uri // empty' "${THALLIUM_CONF_FILE}")"

        if [ -z "${THALLIUM_KEY_NAME}" ];
        then
            THALLIUM_KEY_NAME="$(jq -r '.["'"${THALLIUM_PROFILE_NAME}"'"].key_name // empty' "${THALLIUM_CONF_FILE}")"
        fi
    else
        set -a
        source "${THALLIUM_CONF_FILE}"
        set +a
    fi
fi

if [ -z "${THALLIUM_ROLE_ID}" ];
then
    echo 'Missing variable THALLIUM_ROLE_ID' >&2
    exit 1
fi

if [ -z "${THALLIUM_SECRET_ID}" ];
then
    echo 'Missing variable THALLIUM_SECRET_ID' >&2
    exit 1
fi

if [ -z "${THALLIUM_URI}" ];
then
    echo 'Missing variable THALLIUM_URI' >&2
    exit 1
fi

THALLIUM_ACTION="${THALLIUM_ACTION:-${@:${OPTIND}:1}}"
THALLIUM_KEY_NAME="${THALLIUM_KEY_NAME:-deploy}"
THALLIUM_KEY_FIELDS="${THALLIUM_KEY_FIELDS:-"env"}"
THALLIUM_SET_ENV="${THALLIUM_SET_ENV:-${@:${OPTIND}+1:1}}"
THALLIUM_REGION_NAME="${THALLIUM_REGION_NAME:-${AWS_DEFAULT_REGION}}"
THALLIUM_TRAP_ENABLED="${THALLIUM_TRAP_ENABLED:-true}"

function thallium_func_clean()
{
    if [ -f "${THALLIUM_TMP_FILE}" ];
    then
        rm -f "${THALLIUM_TMP_FILE}"
    fi
}

if [ "${THALLIUM_TRAP_ENABLED}" == "true" ];
then
    trap 'thallium_func_clean' ERR EXIT
fi

function thallium_get_token()
{
    local thallium_data="$(echo '{}'|jq --arg role_id "${THALLIUM_ROLE_ID}" \
                                        --arg secret_id "${THALLIUM_SECRET_ID}" \
                                        '. + {role_id: $role_id, secret_id: $secret_id}')"
    local thallium_token="$(curl -s --data "${thallium_data}" \
                                    --request POST "${THALLIUM_URI}/v1/auth/approle/login"| \
                            jq -r '.auth.client_token')"

    if [ "${thallium_token}" = "null" ];
    then
        echo 'Unable to get Thallium token' >&2
        return 1
    fi

    echo "${thallium_token}"
}

function thallium_aws_configure()
{
    if [ -z "${2}" ];
    then
        local thallium_token="$(thallium_get_token)"
    else
        local thallium_token="${2}"
    fi

    local aws_sts_creds="$(curl -s --header "X-Vault-Token: ${thallium_token}" \
                                   --request POST "${THALLIUM_URI}/v1/aws/sts/${THALLIUM_KEY_NAME}" | \
                           jq -r '.data')"

    if [ "${aws_sts_creds}" = "null" ];
    then
        echo 'Unable to get AWS STS credentials' >&2
        return 1
    fi

    local aws_access_key_id="$(echo "${aws_sts_creds}" | jq -r '.access_key')"
    local aws_secret_access_key="$(echo "${aws_sts_creds}" | jq -r '.secret_key')"
    local aws_session_token="$(echo "${aws_sts_creds}" | jq -r '.security_token')"

    if [ ! -z "${THALLIUM_AWS_PROFILE_NAME}" ];
    then
        local opt_profile="--profile ${THALLIUM_AWS_PROFILE_NAME}"
    else
        local opt_profile=""
    fi

    if [ "${1}" != "env" ];
    then
        aws configure set aws_access_key_id "${aws_access_key_id}" ${opt_profile}
        aws configure set aws_secret_access_key "${aws_secret_access_key}" ${opt_profile}
        aws configure set aws_session_token "${aws_session_token}" ${opt_profile}

        if [ ! -z "${THALLIUM_REGION_NAME}" ];
        then
            aws configure set region "${THALLIUM_REGION_NAME}" ${opt_profile}
        fi
    else
        export AWS_ACCESS_KEY_ID="${aws_access_key_id}"
        export AWS_SECRET_ACCESS_KEY="${aws_secret_access_key}"
        export AWS_SESSION_TOKEN="${aws_session_token}"
        export AWS_DEFAULT_REGION="${THALLIUM_REGION_NAME}"
    fi

    if [ ! -z "${THALLIUM_EXPORT_TOKEN}" ];
    then
        export THALLIUM_TOKEN="${thallium_token}"
    fi

    if [ ! -z "${THALLIUM_EXPORT_VAULT}" ];
    then
        export VAULT_ADDR="${THALLIUM_URI}"
        export VAULT_TOKEN="${thallium_token}"
    fi
}

function terraform_configure()
{
    local thallium_token="$(thallium_get_token)"
    export THALLIUM_TOKEN="${thallium_token}"
    export VAULT_ADDR="${THALLIUM_URI}"
    export VAULT_TOKEN="${thallium_token}"
    export TF_VAR_THALLIUM_UUID="${THALLIUM_UUID}"

    if [ ! -e "${VAULT_BIN}" ] || [ -z "${THALLIUM_KEY_NAME}" ];
    then
        return 0;
    fi

    THALLIUM_TMP_FILE="$(mktemp)"
    for field in "${THALLIUM_KEY_FIELDS[@]}";
    do
        ${VAULT_BIN} kv get -field "${field}" "secret/${THALLIUM_KEY_NAME}" >> "${THALLIUM_TMP_FILE}"
        echo '' >> "${THALLIUM_TMP_FILE}"
        local rc=${?}
        if [ "${rc}" -ne 0 ];
        then
            break
        fi
    done

    if [ "${rc}" -eq 0 ];
    then
        set -a; source ${THALLIUM_TMP_FILE}; set +a;
    else
        return ${rc};
    fi
}

function ansible_configure()
{
    local thallium_token="$(thallium_get_token)"
    export THALLIUM_TOKEN="${thallium_token}"
    export VAULT_ADDR="${THALLIUM_URI}"
    export VAULT_TOKEN="${thallium_token}"

    if [ ! -e "${VAULT_BIN}" ] || [ -z "${THALLIUM_KEY_NAME}" ];
    then
        return 0;
    fi

    THALLIUM_TMP_FILE="$(mktemp)"
    for field in "${THALLIUM_KEY_FIELDS[@]}";
    do
        ${VAULT_BIN} kv get -field "${field}" "secret/${THALLIUM_KEY_NAME}" >> "${THALLIUM_TMP_FILE}"
        echo '' >> "${THALLIUM_TMP_FILE}"
        local rc=${?}
        if [ "${rc}" -ne 0 ];
        then
            break
        fi
    done

    if [ "${rc}" -eq 0 ];
    then
        set -a; source ${THALLIUM_TMP_FILE}; set +a;
    else
        return ${rc};
    fi
}

function thallium_configure()
{
    local thallium_token="$(thallium_get_token)"
    export THALLIUM_TOKEN="${thallium_token}"
    export VAULT_ADDR="${THALLIUM_URI}"
    export VAULT_TOKEN="${thallium_token}"
}

rc=0

case "${THALLIUM_ACTION}" in
    aws-configure)
        thallium_aws_configure "${THALLIUM_SET_ENV}"
    ;;
    aws-ecr-login)
        if [ -z "$(aws configure get aws_session_token)" ];
        then
            thallium_aws_configure "${THALLIUM_SET_ENV}"
        fi
        AWS_VERSION=$(aws --version 2>&1 | grep -oP 'aws-cli/\K[0-9]+')
        if [[ "$AWS_VERSION" == "1" ]]; then
            aws ecr get-login --no-include-email --region "${AWS_DEFAULT_REGION}"|bash
        elif [[ "$AWS_VERSION" == "2" ]]; then
            AWS_ACCOUNT_NUMBER="$(aws sts get-caller-identity --query Account --output text)"
            aws ecr get-login-password --region "${AWS_DEFAULT_REGION}" | docker login --username AWS --password-stdin "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
        else
            echo "Unsupported / no AWS CLI version" >&2
        fi
        rc="$?"
    ;;
    thallium-configure)
        thallium_configure
    ;;
    ansible*)
        ansible_configure
        if [ ${?} -ne 0 ];
        then
            echo 'Unable to load ansible environment' >&2
            exit ${?}
        fi

        ANSIBLE_BIN="${ANSIBLE_BIN:-$(which ${THALLIUM_ACTION})}"
        if [ ! -e "${ANSIBLE_BIN}" ];
        then
            echo 'Unable to find ansible bin file' >&2
            exit 1
        fi
        sudo -H -E ${ANSIBLE_BIN} "${WRAPPER_OPT[@]}"
        rc="$?"
    ;;
    terraform)
        thallium_aws_configure "${THALLIUM_SET_ENV}"
        TERRAFORM_BIN="${TERRAFORM_BIN:-$(which terraform)}"
        if [ ! -e "${TERRAFORM_BIN}" ];
        then
            echo 'Unable to find terraform bin file' >&2
            exit 1
        fi
        ${TERRAFORM_BIN} "${WRAPPER_OPT[@]}"
        rc="$?"
    ;;
    terragrunt)
        terraform_configure
        if [ ${?} -ne 0 ];
        then
            echo 'Unable to load terraform environment' >&2
            exit ${?}
        fi

        thallium_aws_configure "${THALLIUM_SET_ENV}" "${THALLIUM_TOKEN}"
        TERRAGRUNT_BIN="${TERRAGRUNT_BIN:-$(which terragrunt)}"
        if [ ! -e "${TERRAGRUNT_BIN}" ];
        then
            echo 'Unable to find terragrunt bin file' >&2
            exit 1
        fi
        ${TERRAGRUNT_BIN} "${WRAPPER_OPT[@]}"
        rc="$?"
    ;;
    *)
        echo "${USAGE}" >&2
        exit 1
    ;;
esac

if [ ${rc} -ne 0 ];
then
    exit $rc
fi

if [ ! -z "${THALLIUM_OUTENV_FORMAT}" ];
then
    ${ENV_BIN}|${JSON_DOTENV_BIN} -c list -f - --format "${THALLIUM_OUTENV_FORMAT}"
    rc=${?}
fi

if [ ${THALLIUM_SOURCED} -ne 1 ];
then
    exit ${rc}
fi
