#!/usr/bin/env bash
set -e
SQL_PROXY_HOST="${SQL_PROXY_HOST:-localhost}"
SSH_SQL_PROXY_HOST="sqlproxy.${SQL_PROXY_HOST}"
CONNECTION_CACHE="$HOME/.cache/sqlproxy_${SQL_PROXY_HOST}"

HELP="Usage: myssh [ls|connect]\n
SUBCOMMANDS:\n
  ls: list available database hosts\n
  connect: connect to a database host\n
  disconnect: Quit existing ssh master session\n
\n
SYNTAX connect host [-u user] [-p password] [-c client]
"

# Detect Pipe
if ! [ -t 1 ]
then
    USE_CLI=true
fi

ssh_status() {
    ssh -n -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" > /dev/null 2>&1
}

connect() {
    mkdir -p "${HOME}/.ssh/controlmasters"

    if ! ssh_status
    then
        echo "" > "${CONNECTION_CACHE}"
        ssh -n -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q
    fi
}

disconnect() {
    if ssh_status
    then
        ssh -n -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q
    fi
}

# Establishes a port forward to the target host
# args:
# $1 - target ip
# $2 - target port
port_forward() {
    ACTIVE_HOST=$(cat "${CONNECTION_CACHE}")
    if [ -z "${ACTIVE_HOST}" ] || [ "${ACTIVE_HOST}" != "$1:$2" ]
    then
        if [ -n "${ACTIVE_HOST}" ]
        then
            ssh -n -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q
        fi
        ssh -n -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}"
    fi
    echo "$1:$2" > "${CONNECTION_CACHE}"
}

ls_hosts() {
    ssh -n -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" ls
}

get_host() {
    if [ "$1" = '' ]
    then
        printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n'
        exit 1
    else
        TARGET_HOST_DATA=$(ssh -n -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" "get $1")
        if [ "${TARGET_HOST_DATA}" = '' ]
        then
            printf 'No such host: "%s"\n' "$1"
            exit 1
        fi
    fi
}

# Checks and sets sql login variables
# args:
# $1 - sql type (mysql or psql)
# $2 - target ip
# $3 - target port
# $4 - username (optional)
# $5 - password (optional)
set_host_env() {
    # Unquote argument
    ARG="${1//\"/}"
    if [ "${ARG}" = 'mysql' ] || [ "${ARG}" = 'postgres' ]
    then
        TARGET_HOST_TYPE="${ARG}"
    else
        printf 'Invalid Database type: "%s"\n' "$T"
        exit 1
    fi

    ARG="${2//\"/}"
    if [[ "${ARG}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
    then
        TARGET_HOST_IP="${ARG}"
    else
        printf 'Invalid Host IP "%s" given.\n' "${ARG}"
        exit 1
    fi

    ARG="${3//\"/}"
    if [[ "${ARG}" =~ ^[0-9]+$ ]]
    then
       TARGET_HOST_PORT="${ARG}"
    else
        printf 'Invalid Host Port "%s" given.\n' "${ARG}"
        exit 1
    fi

    ARG="${4//\"/}"
    if [ -z "${TARGET_HOST_DBNAME}" ] && [ -n "${ARG}" ]
    then
        TARGET_HOST_DBNAME="${ARG}"
    fi

    ARG="${5//\"/}"
    if [ -z "${TARGET_HOST_USERNAME}" ] && [ -n "${ARG}" ]
    then
        TARGET_HOST_USERNAME="${ARG}"
    fi

    ARG="${6//\"/}"
    if [ -z "${TARGET_HOST_PASSWORD}" ] && [ -n "${ARG}" ]
    then
        TARGET_HOST_PASSWORD="${ARG}"
    fi

}

# Runs the sql client for an active connection
# args:
# $1 - sql type (mysql or psql)
# $2 - username
# $3 - password
# $4 - dbname (optional)
run_client() {
    if [ "$1" = 'mysql' ]
    then
        if [ -d "/Applications/Sequel Ace.app" ] && [ "${USE_CLI}" != true ]
        then
            open "mysql://$2:$3@${SQL_PROXY_HOST}:6033" -a "Sequel Ace"
        elif which mysql >/dev/null 2>&1
        then
            mysql --protocol=TCP -u "$2" -p"$3" -h "${SQL_PROXY_HOST}" "${4:+-D$4}" -P 6033
        else
            SHOW_CLI_HELP=true
        fi
    elif [ "$1" = 'postgres' ]
    then
        if which psql >/dev/null 2>&1
        then
            psql "postgresql://$2:$3@${SQL_PROXY_HOST}:6033/${4:-postgres}"
        else
            SHOW_CLI_HELP=true
        fi
    fi

    if [ "${SHOW_CLI_HELP}" = true ]
    then
        printf 'No %s client binary found.\nYou can maually establish a connection using the following data.\n' "$1"
        printf 'host:\t%s\nport:\t%s\nuser:\t%s\npassword:\t%s\n' "${SQL_PROXY_HOST}" '6033' "$2" "$3"
    fi
}

MAIN_OPTION="$1"
if [ -n "$1" ]
then
    shift
fi

# ensure connection
connect
case "${MAIN_OPTION}" in
    ls)
        ls_hosts;;
    connect)
        # check if host is valid
        TARGET_HOST="$1"
        if [ -n "$1" ]
        then
            shift
        else
            printf 'No host specified.\n'
            exit 1
        fi

        while getopts "u:p:" o
        do
            case "$o" in
                u) TARGET_HOST_USERNAME="${OPTARG}" ;;
                p) TARGET_HOST_PASSWORD="${OPTARG}" ;;
                d) TARGET_HOST_DBNAME="${OPTARG}" ;;
                c) USE_CLI=true ;;
            esac
        done
        get_host "${TARGET_HOST}"
        # Do not quote this.
        set_host_env ${TARGET_HOST_DATA}
        port_forward "${TARGET_HOST_IP}" "$TARGET_HOST_PORT"
        if [ -n "${TARGET_HOST_USERNAME}" ] && [ -n "${TARGET_HOST_PASSWORD}" ]
        then
            run_client "${TARGET_HOST_TYPE}" "${TARGET_HOST_USERNAME}" "${TARGET_HOST_PASSWORD}" "${TARGET_HOST_DBNAME}"
        fi
        ;;
    disconnect)
        disconnect;;
    *)
        echo -e "${HELP}";;
esac