#!/bin/bash
set -e

DATABASE=photowall
DB_PROD=production-cluster.cluster-cfl0zi9wi0ze.eu-west-1.rds.amazonaws.com
DB_STAGE=staging-new-cluster.cluster-cfl0zi9wi0ze.eu-west-1.rds.amazonaws.com
DB_LOCAL=localhost
DUMP_FILE=~/photowall.sql
JOBS=1

throw_err()
{
    echo && echo "    *** Error: $1" && usage 1
}

graceful_exit()
{
    echo && echo "$1" && exit 0
}

usage()
{
    echo
    cat <<- EOF
    Usage: $0 [OPTION]... [COMMAND] [SOURCE] <TARGET>

    Commands:
        dump <source>           dump database photowall from SOURCE
        sync <source> <target>  sync database photowall from SOURCE database to TARGET database

    Options:
        -d, --dry-run           don't actually run any commands, just print them
        -h, --help              display this help message
        -j, --jobs <num>        (DEFAULT: 1) number of processes to use when restoring the dump
        -p, --production        tells if the restored database is production like (stage or prod).
                                If set, additional grants will be handled
EOF
    exit ${1:-0}
}

# Let -i option default to number of CPU cores, use the output of nproc for this.
JOBS=$(nproc)

db_exec()
{
    echo "psql -h $TARGET -xU root -d ${2:-postgres} -c \"$1\""

    if [ -z "$DRY_RUN" ]
    then
        psql -h $TARGET -xU root -d ${2:-postgres} -c "$1"
    fi
}

confirm_sync()
{
    cat <<- EOF
    $(tput blink)
    !!! WARNING !!!$(tput sgr0)$(tput bold)
    DATABASE ${DATABASE}
          ON ${TARGET}
        WILL BE DROPPED AND REPLACED WITH THE ONE
        FROM ${SOURCE}$(tput sgr0)$(tput blink)
    !!! WARNING !!!$(tput sgr0)

EOF

    read -rep "Are you sure this is what you want? [y/N] " -n 1 -r
    [[ ! $REPLY =~ ^[Yy]$ ]] && graceful_exit "Quitting..." || true
}

create_dump()
{
    echo "* Dumping database ${DATABASE} on ${SOURCE} to ${DUMP_FILE}"
    echo
    echo "pg_dump -h ${SOURCE} -d ${DATABASE} -U root -Fc > ${DUMP_FILE}"
    if [ -z "$DRY_RUN" ]
    then
        pg_dump -h $SOURCE -d $DATABASE -U root -Fc > ${DUMP_FILE}
    fi
}

dump()
{
    ## Check if dump file already exists
    if [ -f "$DUMP_FILE" ]
    then
        FDATA=$(date -r $DUMP_FILE "+%Y-%m-%d %H:%M:%S")
        echo
        echo "File $DUMP_FILE ($FDATA) already exists."
        read -rep "Use that instead? [y/N/q] " -n 1 -r
        case "$REPLY" in
            [yY])
                echo
                echo "* Using existing dump file ($FDATA)"
                ;;
            [nN])
                echo
                create_dump
                ;;
            *)
                graceful_exit "Quitting..."
                ;;
        esac
    else
        echo '* Dump file not found'
        echo
        create_dump
    fi
}

provision()
{
    db_exec "
        SELECT pg_terminate_backend(pg_stat_activity.pid)
          FROM pg_stat_activity
         WHERE pg_stat_activity.datname = 'photowall'
           AND pid <> pg_backend_pid();"

    echo "dropdb -h $TARGET -U root --if-exists photowall"
    echo "createdb -h $TARGET -U root --encoding=UTF8 photowall"

    if [ -z "$DRY_RUN" ]
    then
        dropdb -h $TARGET -U root --if-exists photowall
        createdb -h $TARGET -U root --encoding=UTF8 photowall
    fi

    db_exec 'CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA pg_catalog;' photowall
}

restore()
{
    provision
    echo "pg_restore -h $TARGET -U root --no-owner --no-acl -j $JOBS -d photowall $DUMP_FILE"
    if [ -z "$DRY_RUN" ]
    then
        pg_restore -h $TARGET -U root --no-owner --no-acl -d photowall $DUMP_FILE
    fi
}

set_grants()
{
    db_exec 'REVOKE TEMPORARY ON DATABASE photowall FROM public';
    SQL=$(cat <<- 'EOF'
        REVOKE ALL ON SCHEMA public FROM public;
        GRANT USAGE ON SCHEMA public TO photowall;
        GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA public TO photowall;
        GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO photowall;
        ALTER DEFAULT PRIVILEGES IN SCHEMA public
            GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES TO photowall;
        ALTER DEFAULT PRIVILEGES IN SCHEMA public
            GRANT USAGE, SELECT ON SEQUENCES TO photowall;
EOF
)
    db_exec "$SQL" photowall

    if [ ! -z $PRODUCTION ]
    then
        db_exec 'GRANT USAGE ON SCHEMA public TO funnel;' photowall
        db_exec 'GRANT USAGE ON SCHEMA public TO datastudio;' photowall

        for x in $(cat <<-EOF
            v_analytics_products
            v_analytics_inquiries
            v_analytics_discounts
            v_analytics_product_categories
            v_analytics_orders_not_delivered
            v_analytics_orders_rows_not_delivered
            v_analytics_order_rows
            v_analytics_orders2
            v_analytics_contract_customer_discounts
EOF
        )
        do
            db_exec "GRANT SELECT ON ${x} TO datastudio;" photowall
        done

        for x in $(cat <<-EOF
            v_funnel_collections
            v_funnel_discounts
            v_funnel_order_rows
            v_funnel_orders
            v_funnel_pricelist
EOF
        )
        do
            db_exec "GRANT SELECT ON ${x} TO funnel;" photowall
        done

        for x in $(cat <<-EOF
            v_esales_categorytree_i18n
EOF
        )
        do
            db_exec "ALTER MATERIALIZED VIEW ${x} OWNER TO photowall;" photowall
        done
    fi
}

set_source()
{
    SOURCE="$1"
    SOURCE_HASH=$(echo "${SOURCE}${DATABASE}" | md5sum | cut -d' ' -f1)
    DUMP_FILE="/tmp/${DATABASE}-${SOURCE_HASH:0:10}.sql"
}

# Handle arguments
PARAMS=""
while (( "$#" )); do
    case "$1" in
        -d|--dry-run)
            DRY_RUN=1
            shift
            ;;
        -h|--help)
            usage
            ;;
        -j|--jobs)
            if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
                JOBS="$2"
                shift 2
            else
                throw_err "Argument for ${1} is missing"
            fi
            ;;
        -p|--production)
            PRODUCTION=1
            shift 1
            ;;
        -*|--*=)
            throw_err "Unsupported flag ${1}"
            ;;
        *)
            PARAMS="${PARAMS} ${1}"
            shift
            ;;
    esac
done

# reset positional arguments
eval set -- "${PARAMS}"

[ -z "$2" ] && throw_err "Source server URL is missing" || set_source "$2"

case "$1" in
    dump)
        dump
        ;;
    sync)
        [ -z $3 ] && throw_err "Target server URL is missing"
        TARGET="$3"
        confirm_sync
        dump
        restore
        set_grants
        ;;
    *)
        throw_err "${1} is not a valid command"
        ;;
esac

[ -z "$1" ] && throw_err "No command given"
