#!/bin/bash

BACKUP_DIR="/var/tmp/mds-resync-db"
PG_HBA_BACKUP="${BACKUP_DIR}/pg_hba.conf"
REBUILT_DB_WAIT_TIMEOUT=600

#shellcheck disable=SC1091
source /usr/bin/xivo-upgrade-functions
#shellcheck disable=SC1091
source /usr/bin/mds-upgrade-functions

normalize_locale() {
    tr '[:upper:]' '[:lower:]' | sed -e 's/utf-8/utf8/g'
}

warn_about_locale_change() {
    local image="${1}"; shift
    local cluster_locale="${1}"; shift
    local image_locale

    if [ -z "${cluster_locale}" ]; then
        echo "Note: current cluster locale unknown, skipping the locale check."
        return 0
    fi

    image_locale=$(get_image_env_value "${image}" "LANG")
    if [ -z "${image_locale}" ]; then
        echo "Note: locale of ${image} unknown, skipping the locale check."
        return 0
    fi

    if [ "$(echo "${cluster_locale}" | normalize_locale)" != "$(echo "${image_locale}" | normalize_locale)" ]; then
        echo "Note: the database cluster locale (${cluster_locale}) differs from the one of the image (${image_locale})."
        echo "      The recreated cluster will use ${image_locale}."
    fi
}

get_host_pgdata_of_image() {
    local image="${1}"; shift
    local pgdata

    pgdata=$(get_image_env_value "${image}" "PGDATA")
    if [ -z "${pgdata}" ]; then
        echo "ERROR: could not read PGDATA from ${image}." >&2
        return 1
    fi
    if [ ! -d "${pgdata}" ]; then
        echo "ERROR: ${pgdata} is not a directory of this host." >&2
        echo "       The data directory of the db container is expected to be bind mounted on the same path." >&2
        return 1
    fi

    echo "${pgdata}"
}

backup_pg_hba() {
    local pgdata="${1}"; shift

    if [ -f "${PG_HBA_BACKUP}" ]; then
        echo "Keeping ${PG_HBA_BACKUP}, saved by a resynchronization that did not restore it"
        return 0
    fi
    if [ ! -f "${pgdata}/pg_hba.conf" ]; then
        return 0
    fi
    mkdir -p "${BACKUP_DIR}"
    cp -a "${pgdata}/pg_hba.conf" "${PG_HBA_BACKUP}"
    echo "Saved ${pgdata}/pg_hba.conf to ${PG_HBA_BACKUP}"
}

restore_pg_hba() {
    local pgdata="${1}"; shift

    if [ ! -f "${PG_HBA_BACKUP}" ]; then
        return 0
    fi

    cp -a "${PG_HBA_BACKUP}" "${pgdata}/pg_hba.conf"
    echo "Restored ${PG_HBA_BACKUP} to ${pgdata}/pg_hba.conf"
    rm -f "${PG_HBA_BACKUP}"
    xivo-dcomp reload db
}

wipe_pgdata() {
    local pgdata="${1}"; shift

    echo "Stopping the database container..."
    xivo-dcomp stop db
    xivo-dcomp rm -f db > /dev/null

    echo "Removing the PostgreSQL data directory ${pgdata}..."
    rm -rf "${pgdata:?}"

    if [ -e "${pgdata}" ]; then
        echo "ERROR: ${pgdata} could not be removed." >&2
        return 1
    fi
}

wait_for_rebuilt_db() {
    local waited

    echo -n "Waiting up to ${REBUILT_DB_WAIT_TIMEOUT}s for the rebuilt database..."
    for ((waited = 0; waited < REBUILT_DB_WAIT_TIMEOUT; waited++)); do
        if is_postgres_running_on_port; then
            echo
            return 0
        fi
        sleep 1
        echo -n "."
    done
    echo
    echo "ERROR: the rebuilt database did not answer within ${REBUILT_DB_WAIT_TIMEOUT}s." >&2
    return 1
}

recreate_db() {
    echo "Recreating the database at the new schema..."
    if ! xivo-dcomp start-db; then
        echo "ERROR: the database container could not be started." >&2
        return 1
    fi
    wait_for_rebuilt_db
}

ask_to_continue_resync() {
    local answer

    echo ""
    echo "The PostgreSQL data directory of this media server is about to be ERASED."
    echo "Its database will be rebuilt from the XiVO Main; the local call_log table"
    echo "and the cel and queue_log rows not sent to the XiVO CC yet will be lost."
    read -r -p 'Would you like to continue [N/y]? ' answer
    answer="${answer:-N}"
    if [ "$answer" != 'y' ] && [ "$answer" != 'Y' ]; then
        exit 0
    fi
}

stop_services() {
    echo "Stopping the media server services..."
    xivo-service stop all
}

start_services() {
    local rebuild_started="${1}"; shift

    echo "Starting the media server services..."
    xivo-dcomp up -d --remove-orphans
    wait_for_initial_copy "${rebuild_started}"
    xivo-service start
    catch_up_initial_copy "${rebuild_started}"
}

leave_services_stopped() {
    xivo-service stop all
    display_mds_database_recovery
}

db_is_subscribed_to_main() {
    local subscriptions

    subscriptions=$(count_subscriptions)

    [ -n "${subscriptions}" ] && [ "${subscriptions}" -gt 0 ]
}

display_subscription_enable_failure() {
	cat <<-EOF
	******************************************************************************
	*  ERROR: the subscription to the XiVO Main could not be enabled.            *
	*                                                                            *
	*  The local replica is EMPTY and will stay empty: this media server must    *
	*  not be put back in service as is.                                         *
	*                                                                            *
	*  You MUST:                                                                 *
	*    1. enable it once the database answers:                                 *
	*         xivo-enable-mds-subscription                                       *
	*    2. or re-run mds-resync-db -f -s                                        *
	******************************************************************************
	EOF
}

display_subscription_creation_failure() {
	cat <<-EOF
	******************************************************************************
	*  ERROR: the database was rebuilt but no subscription to the XiVO Main      *
	*  was created.                                                              *
	*                                                                            *
	*  The local replica is EMPTY and will stay empty: this media server must    *
	*  not be put back in service as is. The usual cause is a replication slot   *
	*  left behind on the Main, since CREATE SUBSCRIPTION also creates a slot    *
	*  of the same name and fails when it already exists.                        *
	*                                                                            *
	*  You MUST:                                                                 *
	*    1. on the XiVO Main, list the surviving slots:                          *
	*         SELECT slot_name FROM pg_replication_slots;                        *
	*       and drop the one named after this media server:                      *
	*         SELECT pg_drop_replication_slot('main_<MDS_NAME>');                *
	*    2. and then, re-run mds-resync-db -f -s                                 *
	******************************************************************************
	EOF
}

is_db_image_present() {
    local image="${1}"; shift

    docker image inspect "${image}" > /dev/null 2>&1
}

resync_db() {
    local image="${1}"; shift
    local pgdata="${1}"; shift
    local cluster_locale

    cluster_locale="${MDS_CLUSTER_LOCALE:-$(get_db_locale)}"
    warn_about_locale_change "${image}" "${cluster_locale}"

    backup_pg_hba "${pgdata}"

    display_upgrade_notice "Rebuilding the media server database..."
    wipe_pgdata "${pgdata}" || return "${RESYNC_FAILED_ERASED}"

    if ! recreate_db; then
        echo "Check the db container with: xivo-dcomp logs db" >&2
        return "${RESYNC_FAILED_ERASED}"
    fi
    restore_pg_hba "${pgdata}"

    if ! db_is_subscribed_to_main; then
        display_subscription_creation_failure
        return "${RESYNC_FAILED_ERASED}"
    fi

    if ! xivo-enable-mds-subscription; then
        display_subscription_enable_failure
        return "${RESYNC_FAILED_ERASED}"
    fi

    echo "The database was recreated and re-subscribed to the XiVO Main."
    echo "The initial copy runs in the background."
}

usage() {
    local progname="${1}"; shift

    echo "usage: ${progname} [--no-service-control] [-f] [-s] [-h]"
    echo ""
    echo "Erase the PostgreSQL data directory of this media server and let the db"
    echo "container rebuild it from the XiVO Main, subscription included."
    echo "The db image already present on this media server is used, none is pulled."
    echo ""
    echo -e "\t--no-service-control: do not stop/start the services (they already are stopped)"
    echo -e "\t-f: skip the upgrade order check and the confirmation"
    echo -e "\t-s: go on even if the statistics replication to the XiVO CC is stalled: the rows not sent yet are lost"
    echo -e "\t-h: print usage"
}

main() {
    local progname="mds-resync-db"
    local service_control=1
    local force=0
    local ignore_stats=0
    local image pgdata rebuild_started outcome

    while [ $# -gt 0 ]; do
        case "${1}" in
            --no-service-control) service_control=0;;
            -f) force=1;;
            -s) ignore_stats=1;;
            -h)
                usage "${progname}"
                exit 0
            ;;
            *)
                echo "${progname} : option ${1} is not valid" >&2
                usage "${progname}"
                exit 1
            ;;
        esac
        shift
    done

    if ! image=$(get_db_image); then
        echo "ERROR: could not resolve the database image from the docker compose configuration." >&2
        exit 1
    fi

    if ! is_db_image_present "${image}"; then
        echo "ERROR: ${image} is not present on this media server, pull it with: xivo-dcomp pull db" >&2
        exit 1
    fi

    if [ "${force}" -eq 0 ]; then
        display_upgrade_notice "Checking that the XiVO Main was upgraded first..."
        if ! check_upgrade_order "${image}"; then
            exit 1
        fi
    fi

    if ! command -v xivo-enable-mds-subscription > /dev/null; then
        echo "ERROR: xivo-enable-mds-subscription is missing, install it with: apt install xivo-manage-db-mds" >&2
        exit 1
    fi

    if ! pgdata=$(get_host_pgdata_of_image "${image}"); then
        exit "${RESYNC_FAILED_UNTOUCHED}"
    fi

    if [ "${service_control}" -eq 1 ]; then
        if ! check_mds_stats_replication "${force}" "${ignore_stats}" 1; then
            echo "Resynchronization aborted: nothing was stopped nor erased." >&2
            exit 1
        fi
        [ "${force}" -eq 0 ] && ask_to_continue_resync
    fi

    if ! release_main_replication_slot_if_db_answers; then
        echo "ERROR: the replication slot of this media server on the XiVO Main could not be released: nothing was erased." >&2
        exit "${RESYNC_FAILED_UNTOUCHED}"
    fi

    if [ "${service_control}" -eq 1 ]; then
        stop_services
    fi

    rebuild_started=$(date +%s)
    resync_db "${image}" "${pgdata}"
    outcome=$?

    if [ "${outcome}" -eq "${RESYNC_FAILED_ERASED}" ]; then
        leave_services_stopped
    elif [ "${service_control}" -eq 1 ]; then
        [ "${outcome}" -eq 0 ] || rebuild_started=""
        start_services "${rebuild_started}"
    fi
    exit "${outcome}"
}

main "${@}"
