#!/bin/bash

# ------------------------------------- USAGE ----------
function USAGE {
cat<<ENDUSAGE
Utility for manipulating DB

USAGE: db-conf COMMAND [OPTIONS]

    create         initialize remote database
    drop           try to drop remote database (fail if any connection exists)
    update         run existing SQL scripts to update remote DB to the latest version
    
    redis-cli      provide Redis command line interface
    flush-redis    flush Redis database
    
    sh             interactive access to Postgres DB (apl user)
    admsh          interactive access to Postgres DB (admin  user)
    cmd <string>   specify SQL command string to execute (apl user)
    source <file>  Use the file as the source of commands
    
    sync           synchronize DB object attributes with template files

OPTIONS:
    -d=DB          Database name
    -v=version     Use SQL scripts for specific release version

ENDUSAGE
}

export AUDIT_DB_DBNAME=audit

SCRIPTDIR=$(dirname $0)

. $SCRIPTDIR/_environment
. $SCRIPTDIR/_functions
. $SCRIPTDIR/_conf_utils

CMD=$1
[ -z "$CMD" ] && USAGE && die 1 "Need at least one argument"
shift

for ARG in "$@"; do
    case $ARG in
        -v=*|--version=*)
            VERSION="${ARG#*=}"
            ;;
        -d=*|--db=*)
            DATABASE="${ARG#*=}"
            ;;
    esac
done

if [ -z "$VERSION" ]; then
    ROOT=$BASE_DIR
else
    ROOT=$STAGE_DIR/$VERSION
fi
[ ! -d $ROOT/db ] && die 99 "$ROOT/db not found"
[ ! -d $ROOT/etc ] && die 99 "$ROOT/etc not found"

# Check DATABASE variable
# It should contain databse name, 'apl' is used by default
if [ -z "$DATABASE" ]; then
    DATABASE=$OVERCAST_DB_DBNAME
fi
export DATABASE
echo "======================================"
echo "Using DATABASE: $DATABASE"

DBDIR=$ROOT/db/$DATABASE
if [ ! -d $DBDIR ]; then
    # Pre-1.3.0
    DBDIR=$ROOT/db
fi

. $ROOT/etc/base.conf
. $ROOT/etc/env.conf

function db-drop {
    export PGPASSWORD=$OVERCAST_DB_ADMIN_PASSWD
    PSQL_CMD="psql -U $OVERCAST_DB_ADMIN_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -d postgres"
    $PSQL_CMD -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$DATABASE' AND pid <> pg_backend_pid();"
    $PSQL_CMD -c "DROP DATABASE $DATABASE;"
    $PSQL_CMD -c "DROP ROLE apl;"
    #$PSQL_CMD -c "DROP ROLE druid;"
}

function db-create {
    export PGPASSWORD=$OVERCAST_DB_ADMIN_PASSWD
    PSQL_CMD="psql -U $OVERCAST_DB_ADMIN_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -d postgres"
    # Get 'apl' role password from env.conf
    # Create 'apl' database if doesn't exist
    if [ ! $($PSQL_CMD -Atl | grep ^$DATABASE) ]; then
        $PSQL_CMD -c "CREATE DATABASE $DATABASE TEMPLATE template0 ENCODING 'UTF-8' LC_CTYPE 'en_US.utf8' LC_COLLATE 'en_US.utf8'"
    fi
    # Create 'apl' user if doesn't exist
    if [ ! $($PSQL_CMD -At -c "SELECT usename FROM pg_catalog.pg_user WHERE usename='apl'" | grep apl) ]; then
        $PSQL_CMD -c "CREATE ROLE apl NOSUPERUSER NOCREATEDB NOCREATEROLE LOGIN PASSWORD '$OVERCAST_DB_SERVICE_PASS'"
    fi
    
    cat <<EOF | envsubst | kubectl apply -f -

# ======================== PostgreSQL =======================
apiVersion: v1
kind: Service
metadata:
  name: overcast-db
  labels:
    app: overcast
spec:
  type: ExternalName
  externalName: $OVERCAST_DB_INTERNAL_IP
  ports:
  - name: postgresql
    protocol: TCP
    port: $OVERCAST_DB_EXTERNAL_PORT
    targetPort: 5432
---
# ========================== Redis =========================
kind: Service
apiVersion: v1
metadata:
  name: overcast-redis
  labels:
    app: overcast
spec:
  type: ExternalName
  externalName: $OVERCAST_REDIS_EXTERNAL_IP
  ports:
    - name: redis
      protocol: TCP
      port: $OVERCAST_REDIS_EXTERNAL_PORT
      targetPort: 6379
EOF
}

function deploy-flyway {
    FLYWAYDIR=$DBDIR/flyway
    if [ -d $FLYWAYDIR ]; then
        unlink $SCRIPTDIR/flyway
        ln -sf $FLYWAYDIR/flyway $SCRIPTDIR/flyway
        return 0;
    else
        local TMPDIR=$DBDIR/tmp
        mkdir -p $TMPDIR
        rm -rf $TMPDIR/*
        pushd $TMPDIR
        wget "https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/4.2.0/flyway-commandline-4.2.0.tar.gz"
        tar -xzf flyway-commandline-4.2.0.tar.gz
        mv flyway-4.2.0 $DBDIR/flyway
        rm -rf $TMPDIR
        ln -sf $FLYWAYDIR/flyway $SCRIPTDIR/flyway
        popd
    fi
}

function db-update {
    deploy-flyway
    
    local OVERWRITE=1
    FLYWAY_URL="jdbc:postgresql://$OVERCAST_DB_EXTERNAL_IP:$OVERCAST_DB_EXTERNAL_PORT/$DATABASE"
    FLYWAY_USER=$OVERCAST_DB_ADMIN_USER
    FLYWAY_PASSWD=$OVERCAST_DB_ADMIN_PASSWD
    FLYWAY_LOCATIONS=filesystem:$(cd $DBDIR; pwd)/sql
    subst-and-copy-file $DBDIR/etc/flyway.conf $FLYWAYDIR/conf/flyway.conf
    
    $SCRIPTDIR/flyway migrate
}

function db-repair {
    deploy-flyway
    
    local OVERWRITE=1
    FLYWAY_URL="jdbc:postgresql://$OVERCAST_DB_EXTERNAL_IP:$OVERCAST_DB_EXTERNAL_PORT/$DATABASE"
    FLYWAY_USER=$OVERCAST_DB_ADMIN_USER
    FLYWAY_PASSWD=$OVERCAST_DB_ADMIN_PASSWD
    FLYWAY_LOCATIONS=filesystem:$(cd $DBDIR; pwd)/sql
    subst-and-copy-file $DBDIR/etc/flyway.conf $FLYWAYDIR/conf/flyway.conf
    
    $SCRIPTDIR/flyway repair
}


function db-sync {
    api=$(kubectl get pod|grep api-php | head -n1 | grep -v grep | grep -v deploy | cut -f 1 -d ' ')
    [ -z "$api" ] && die 2 "Cannot find 'api-php' container"
    
    # Get realms
    realm_list=$(db-cmd "SELECT obj FROM _objs WHERE type='realm' AND deleted=0")
    for realm in $realm_list; do
        echo ------------------------------------------------------------------------------
        echo Syncing templates for realm: $realm
        echo ------------------------------------------------------------------------------
        echo
        export realm
        kubectl exec $api -- curl -X POST -H "X-session: $OVERCAST_LOGIN_HEADER" -H "Content-Type: application/json" -d "{\"realmid\":\"$realm\"}" http://localhost:8000/api/call/syncConf |python -mjson.tool
    done
}

function db-admsh {
    export PGPASSWORD=$OVERCAST_DB_ADMIN_PASSWD
    psql -U $OVERCAST_DB_ADMIN_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT $DATABASE
}

function reset-passwd {
	# 
    export PGPASSWORD=$OVERCAST_DB_ADMIN_PASSWD
    psql -U $OVERCAST_DB_ADMIN_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT $DATABASE -c "update apl.password_history set changed = NOW() where userid = (select obj from apl._objs where name='$1') and changed = (select changed from apl.password_history where userid = (select obj from apl._objs where name='$1') ORDER BY changed DESC LIMIT 1 );"
}

function db-sh {
    export PGPASSWORD=$OVERCAST_DB_SERVICE_PASS
    psql -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT $DATABASE
}

function db-source {
    source=$1
    export PGPASSWORD=$OVERCAST_DB_ADMIN_PASSWD
    psql -U $OVERCAST_DB_ADMIN_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -f $source $DATABASE
}

function db-cmd {
    cmd="$1"
    export PGPASSWORD=$OVERCAST_DB_SERVICE_PASS
    psql -At -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -c "$cmd" $DATABASE
}

function db-backup {
    local bckdir=$SCRIPTDIR/../db/backup/$CLUSTER_ENV/"$(date -u +%s)"
    mkdir -p $bckdir
    export PGPASSWORD=$OVERCAST_DB_SERVICE_PASS
    pg_dump -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -n confdb $DATABASE > $bckdir/confdb.db
    pg_dump -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -n transdb $DATABASE > $bckdir/transdb.db
    #pg_dump -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT -n apl $DATABASE > $bckdir/apl.db
}

function redis-cli {
    pod=$(kubectl get pod|grep cm-overlord|grep Running|tail -1|cut -d' ' -f1)
    [ -z "$pod" ] && die 11 "No cm-overlord instance to call"
    kubectl exec -it $pod -- redis-cli -h overcast-redis
}

function flush-redis {
    api=$(kubectl get pod|grep api-php | head -n1 | grep -v grep | grep -v deploy | cut -f 1 -d ' ')
    [ -z "$api" ] && die 2 "Cannot find 'api-php' container"
    kubectl exec $api -- curl -X POST -H "X-session: $OVERCAST_LOGIN_HEADER" -H "Content-Type: application/json" http://localhost:8000/api/call/flushRedisCache |python -mjson.tool
}

function dump-cert {
    dumpdir=$1
    [ -z "$dumpdir" ] && dumpdir=.
    export PGPASSWORD=$OVERCAST_DB_SERVICE_PASS
    psql -At -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT \
        -c "SELECT val FROM _obj_attr WHERE obj=identity_objid() AND attr='SSL_PRIVATE_KEY'" $DATABASE > $dumpdir/tls.key
    if [ $? == 0 ]; then
        echo Private key written to $dumpdir/tls.key
    else
        echo Error dumping private key
    fi
    psql -At -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT \
        -c "SELECT val FROM _obj_attr WHERE obj=identity_objid() AND attr='SSL_CERTIFICATE'" $DATABASE > $dumpdir/tls.crt
    if [ $? == 0 ]; then
        echo Certificate written to $dumpdir/tls.crt
    else
        echo Error dumping certificate
    fi
}

function push-cert {
    privkey=$1
    cert=$2
    [ -f "$privkey" ] || die 3 "Invalid path to private key file"
    [ -f "$cert" ] || die 3 "Invalid path to certificate file"
    privkey_val="$(cat $privkey)"
    cert_val="$(cat $cert)"
    ssl_time=$(date +'%s %Y-%m-%d %T.%N') 
    [ -z "$privkey_val" ] && die 3 "Empty private key"
    [ -z "$cert_val" ] && die 3 "Empty certificate"
    export PGPASSWORD=$OVERCAST_DB_SERVICE_PASS
    cat << EOF | psql -U $OVERCAST_DB_SERVICE_USER -h $OVERCAST_DB_EXTERNAL_IP -p $OVERCAST_DB_EXTERNAL_PORT $DATABASE
\set AUTOCOMMIT OFF
DELETE FROM _obj_attr WHERE obj=identity_objid() AND attr IN ('SSL_PRIVATE_KEY','SSL_CERTIFICATE','SSL_TIME');
INSERT INTO _obj_attr (obj,attr,val) VALUES (identity_objid(),'SSL_PRIVATE_KEY','$privkey_val');
INSERT INTO _obj_attr (obj,attr,val) VALUES (identity_objid(),'SSL_CERTIFICATE','$cert_val');
INSERT INTO _obj_attr (obj,attr,val) VALUES (identity_objid(),'SSL_TIME','$ssl_time');
COMMIT;
EOF
}


case $CMD in
    create)
        db-create
        ;;
    drop)
        db-drop
        ;;
    update)
        db-update
        ;;
    repair)
        db-repair
        ;;
    sync)
        db-sync
        ;;
    sh)
        db-sh
        ;;
    admsh)
        db-admsh
        ;;
    reset-passwd)
        reset-passwd "$1"
        ;;
    source)
        db-source $1
        ;;
    cmd)
        db-cmd "$1"
        ;;
    backup)
        db-backup
        ;;
    redis-cli)
        redis-cli
        ;;
    flush-redis)
        flush-redis
        ;;
    dump-cert)
        dump-cert "$1"
        ;;
    push-cert)
        push-cert "$1" "$2"
        ;;
    *)
        USAGE
        die 1 "COMMAND not recognized"
esac
echo
