#!/bin/bash

# iff executed under VERBOSE=yes provide more information
verbose() {
  if [[ "$VERBOSE" == "yes" ]] ; then
    echo "$*"
  fi
}

usage() {
  echo -e "Usage: $0 [OPTION]..." 1>&2
  echo -e "\t-f: Force update, even if running on active node" 1>&2
}

FORCE_ACTIVE_NODE=false
RUN_SYNC_DB=false
SYNC_DB="/usr/sbin/ngcp-sync-db"
NOT_REPLICATED_REV_APPLIED=false

while getopts "fh" opt; do
    case "${opt}" in
        f)
            FORCE_ACTIVE_NODE=true
            ;;
        h)
            usage; exit 0;
            ;;
        *)
            usage; exit 1;
            ;;
    esac
done
shift $((OPTIND-1))

nodename=$(/usr/sbin/ngcp-nodename 2>/dev/null || echo "unknown")
if [ "${nodename}" = "unknown" ] ; then
  echo "Error: nodename could not be identified." >&2
  exit 1
fi

service mysql start || true
. /etc/mysql/sipwise.cnf

. /etc/default/ngcp-roles
. /etc/default/ngcp-db

if [ -z "${SIPWISE_DB_PASSWORD}" ] ; then
  echo 'Error: SIPWISE_DB_PASSWORD is unset (using /etc/mysql/sipwise.cnf).' >&2
  exit 1
fi

# check connection with sipwise user
if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'SELECT 1;' 1>/dev/null 2>/dev/null ; then
  echo 'Error: cant connect to local MySQL with sipwise user' >&2
  exit 1
fi

# support automated installation
if [ -n "$AUTOMATED_INSTALL_MODE" ] ; then
  echo "Running in automated installation mode, ignoring check for empty db_schema."
else
  if [ "$(mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'select * from ngcp.db_schema;' 2>/dev/null)" = "" ] ; then
    echo "================================================================="
    echo "Warning: the db_schema table of the ngcp database is empty."
    echo "Are you sure you want to proceed with applying db-schema changes?"
    echo "This will DROP and then re-initialize your existing database."
    printf "Please type 'agree' to really continue: "

    unset AGREE
    read AGREE
    if [ "$AGREE" != "agree" ]; then
      echo "Exiting as requested." >&2
      exit 1
    fi
  fi
fi

if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'use ngcp;' 2>/dev/null ; then
  mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" < /usr/share/ngcp-db-schema/db_scripts/init/0005_create_ngcp.up
fi

if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'use ngcp;' 2>/dev/null ; then
  echo 'Error: ngcp database does not exist.' >&2
  exit 1
fi

if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" ngcp -e 'describe db_schema'  >/dev/null; then
  echo 'Error: db_schema table does not exit.' >&2
  exit 1
fi

if [ -z "$SKIP_SYNC_DB" ] && [ "$NGCP_TYPE" = "carrier" ] && [ "$NGCP_IS_PROXY" = "yes" ] ; then
  if ! $FORCE_ACTIVE_NODE && [ ! -x "$SYNC_DB" ] ; then
    echo "Error: ${SYNC_DB} is required for carrier proxy (or lacking execute permissions)." >&2
    exit 1
  fi
  RUN_SYNC_DB=true
fi

running_on_active_node() {
  if [ "$nodename" = "spce" ] ; then
    return 1
  else
    if /usr/sbin/ngcp-check-active -q ; then
      if $FORCE_ACTIVE_NODE; then
        echo "Force update on active node as requested."
      else
        echo "This seems to be the active node, nothing to do."
      fi
      return 0
    else
      echo "This seems to be the inactive node, applying revisions."
      return 1
    fi
  fi
}

if running_on_active_node ; then
  ACTIVE_NODE=true
else
  ACTIVE_NODE=false
fi

apply_revision() {
  [ -n "$1" ] || return 1

  rev="$1"
  revname="$(basename "$rev")"

  printf "Applying revision script %s: " "$rev"
  if mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" < "$rev" ; then
    echo "done"
  else
    echo "failed. :(" >&2
    echo "Please resolve the problem and run ngcp-update-db-schema again." >&2
    exit 1
  fi

  if mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" ngcp -e "insert into db_schema values (0, '${revname}', '${nodename}', CURRENT_TIMESTAMP);" ; then
    verbose "Marked revision $rev as applied."
  else
    echo "Error while executing DB commands using revision $rev for node $nodename" >&2
    exit 1
  fi
}

# execute the rev script iff there's no entry for *any* host yet
apply_revs() {
  [ -n "$1" ] || return 1

  revs="$1"

  # generate list of rev scripts that are node specific, used inside
  # missing_revision's ngcp-check-rev-applied ... --node $nodename
  for rev in $revs ; do
    revision_file="$(find /usr/share/ngcp-db-schema/ -name "$rev")"

    case "$revision_file" in
      *_not_replicated.up)
        node_revs="$node_revs $rev"
        ;;
    esac
  done

  # shellcheck disable=SC2086
  for missing_revision in \
    $( {
        ngcp-check-rev-applied --schema db_schema --revision $revs                         | awk '/^No match for revision/ {print $5}' ;
        ngcp-check-rev-applied --schema db_schema --revision $node_revs --node "$nodename" | awk '/^No match for revision/ {print $5}' ;
       } | sort -n -u ) ; do
    revision_file="$(find /usr/share/ngcp-db-schema/ -name "$missing_revision")"

    # execute the rev script iff there's no entry for the *current* host yet
    case "$revision_file" in
      *_not_replicated.up)
        if ngcp-check-rev-applied --schema db_schema --revision "$missing_revision" --node "$nodename" | grep -q 'already executed' ; then
          continue
        fi
        ;;
    esac

    if [ -r "$revision_file" ] ; then
      apply_revision "$revision_file"
      if [[ $revision_file == *_not_replicated.up ]] ; then
        NOT_REPLICATED_REV_APPLIED=true
      fi
    else
      echo "Warning: missing revision $missing_revision identified but could not find according db-schema file."
    fi
  done
}


revision_wrapper() {
  [ -n "$1" ] || return 1

  local revlist

  for rev in "$@" ; do

    if ! [ -r "$rev" ] ; then
      echo "Error: $rev can not be read." >&2
      exit 1
    fi

    cd "$(dirname "$rev")" || exit 1 # would fail if a script references a file (like language_strings.txt) in its CWD

    revname="$(basename "$rev")"

    case "$revname" in
      # the scripts that should be executed on *all* hosts, no matter whether
      # they are active or inactive, since the script's content doesn't get replicated
      # NOTE: the actual logic is inside the apply_revs to avoid *_not_replicated.up
      # scripts being executed independent from the other ones
      *_not_replicated.up)
        revlist="$revlist $(basename "$rev")"
        ;;
      *)
        if $ACTIVE_NODE ; then
	  if $FORCE_ACTIVE_NODE; then
            revlist="$revlist $(basename "$rev")"
	  else
            verbose "Replication script ${revname} noted, nothing to do on active node"
	  fi
        else
          revlist="$revlist $(basename "$rev")"
        fi
        ;;
    esac

  done

  apply_revs "$revlist"
}

# make sure we get sorted 10XXX after 9XXX
cd /usr/share/ngcp-db-schema/db_scripts/base/
# shellcheck disable=SC2035 disable=SC2046
revision_wrapper $(printf '%s\0' *.up | sort -z -n | while IFS= read -r -d "" file; do \
                   echo "/usr/share/ngcp-db-schema/db_scripts/base/$file"; done)

cd /usr/share/ngcp-db-schema/db_scripts/diff/
# shellcheck disable=SC2035 disable=SC2046
revision_wrapper $(printf '%s\0' *.up | sort -z -n | while IFS= read -r -d "" file; do \
                   echo "/usr/share/ngcp-db-schema/db_scripts/diff/$file"; done)

# for carrier prx* hosts sync master-slave replication for not_replicated statements (if any)
if $RUN_SYNC_DB && ($NOT_REPLICATED_REV_APPLIED || [ -n "$AUTOMATED_INSTALL_MODE" ]); then
  if [ -x "$SYNC_DB" ] ; then
      PRX_SYNC_DBS="ngcp billing carrier kamailio provisioning prosody mysql"
      PRX_IGNORE_TABLES="kamailio.voicemail_spool"
      EXTRA_SYNC_DB_OPTS=""
      if [ -n "$AUTOMATED_INSTALL_MODE" ] ; then
          EXTRA_SYNC_DB_OPTS="--local-user=root --set-local-grants"
      fi

      # determine the preferred db node a or b if the default db01 is the constants
      MASTER_NODE_PREF=""
      if [ "$CENTRAL_DBHOST" == "db01" ] ; then
        NODE_SIDE="a"
        if [[ "$nodename" == *b ]] ; then
          NODE_SIDE="b"
        fi
        MASTER_NODE_PREF="db01${NODE_SIDE}"
      fi
      if [ -n "$MASTER_NODE_PREF" ] ; then
        EXTRA_SYNC_DB_OPTS="$EXTRA_SYNC_DB_OPTS --master-host $MASTER_NODE_PREF"
      fi

      # shellcheck disable=SC2086
      if /usr/sbin/ngcp-sync-db --verbose --force --use-central-db --repl-mode "master-slave" --databases "${PRX_SYNC_DBS}" --ignore-tables "${PRX_IGNORE_TABLES}" --ssh-tunnel=33125 ${EXTRA_SYNC_DB_OPTS} --local-host=${LOCAL_DBHOST} --local-port=${LOCAL_DBPORT}; then
          echo "Syncing 'not_replicated' statements from the central node."
      else
          echo "Error: While syncing 'not_replicated' statements from the central node, please run ngcp-sync-db-wrapper manually to fix them." >&2
          exit 1
      fi
  else
      echo "Warning: Skipping sync of 'not_replicated' statements from the central node as ${SYNC_DB} is missing on the current node." >&2
  fi
fi

## END OF FILE #################################################################