#!/bin/bash

set -e

FORCE=false
APPLY=false
opts_format='hfa'

die() {
  local message="$*"

  echo "ERROR: ${message}" >&2
  exit 1
}

usage() {
  echo "
Tool to migrate the key-value database from KeyDB to Valkey

Usage: $0 [<option>...] valkey|keydb

Options:
  -h      This help.
  -f      Do not check if flavor is already set.
  -a      Apply the change without committing anything new.
          It is intented to the executed only after the
          management node has be already migrated.
"
}

while getopts "${opts_format}" OPT; do
  case "${OPT}" in
    h)
      usage
      exit 0
    ;;
    f)
      FORCE=true
    ;;
    a)
      APPLY=true
    ;;
  esac
done
shift $((OPTIND - 1))

if [[ $# -ne 1 ]] ; then
  die "missing flavor argument"
fi

new_flavor=${1}
case "${new_flavor}" in
  valkey|keydb)
    :
  ;;
  *)
    die "unknown new flavor: '${new_flavor}'"
  ;;
esac

NGCP_TYPE="$(ngcpcfg get general.ngcp_type)"
if [[ "${NGCP_TYPE}" == 'carrier' || "${NGCP_TYPE}" == 'sppro' ]]; then
  if ngcp-check-active -q; then
    die "This is the active node while it's supposed to run this script on standby one"
  fi
  ngcpcfg pull
fi

cur_flavor=$(ngcpcfg get database.key_value.flavor)

case "${cur_flavor}" in
  valkey)
    if ! "${APPLY}"; then
      cur_flavor=valkey
    else
      cur_flavor=keydb
    fi
  ;;
  keydb)
    if ! "${APPLY}"; then
      cur_flavor=keydb
    else
      cur_flavor=valkey
    fi
  ;;
  *)
    die "unknown current flavor: '${cur_flavor}'"
esac

if ! "${FORCE}"; then
  if [[ "${new_flavor}" == "${cur_flavor}" ]]; then
    echo "INFO: Nothing to do, already switched to '${new_flavor}'"
    exit 0
  fi
fi

if [[ "${new_flavor}" == 'keydb' ]]; then
  echo "INFO: The Valkey database is not compatible with KeyDB, it cannot be moved back."
  echo "INFO: This means that proceeding will initialize an empty database on KeyDB."
  echo "INFO: A backup of the Valkey database will be kept around, but will be overwritten"
  echo "INFO: on the next flavor change."
  read -r -p "Do you want to proceed anyway without moving the Valkey database? [y/N] " a
  case "$a" in
    y|Y|yes|YES)
      ;;
    *)
      die "Aborting redis flavor migration back to KeyDB."
      ;;
  esac
  unset a
fi

services() {
  local flavor=$1

  case "${flavor}" in
    valkey)
      srvs+=(valkey)
      if [[ "${NGCP_TYPE}" == 'carrier' || "${NGCP_TYPE}" == 'sppro' ]]; then
        srvs+=(valkey-master)
        srvs+=(valkey-norep)
      fi
    ;;
    keydb)
      srvs+=(keydb)
      if [[ "${NGCP_TYPE}" == 'carrier' || "${NGCP_TYPE}" == 'sppro' ]]; then
        srvs+=(keydb-norep)
      fi
    ;;
  esac
}

# Stop the master service to replicate from the local one
# shellcheck source=/dev/null
if [ -f /etc/default/ngcp-roles ] ; then
  source /etc/default/ngcp-roles
fi
if [[ -z "${NGCP_PEERNAME}" ]]; then
  die "NGCP_PEERNAME is not set"
fi
echo "INFO: stopping the remote '${NGCP_PEERNAME}' master ${cur_flavor} service to replicate from the current node"
ngcp-ssh "${NGCP_PEERNAME}" "valkey-cli REPLICAOF NO ONE" || true

# Prepare folders and migrate backups
declare -a srvs=()
services "${cur_flavor}"
echo "INFO: stop key_value services for ${cur_flavor}"
ngcp-service stop "${srvs[@]}" || true

if [[ ! -d "/ngcp-data/${new_flavor}/" ]]; then
  mkdir -p "/ngcp-data/${new_flavor}/"
  chown "${new_flavor}:${new_flavor}" "/ngcp-data/${new_flavor}/"
fi

if [[ -d /ngcp-data/"${cur_flavor}" ]] && [[ "${new_flavor}" != 'valkey' ]]; then
  echo "INFO: copy previous data from ${cur_flavor}"
  find "/ngcp-data/${cur_flavor}" -type f -name '*.aof' \
    -exec cp {} "/ngcp-data/${new_flavor}/" \;
  find "/ngcp-data/${cur_flavor}" -type f -name '*.rdb' \
    -exec cp {} "/ngcp-data/${new_flavor}/" \;
  find "/ngcp-data/${new_flavor}" -type f -print \
    -exec chown "${new_flavor}:${new_flavor}" {} \;
fi

# Migrate implementation
if ! "${APPLY}"; then
  ngcpcfg set /etc/ngcp-config/constants.yml "database.key_value.flavor=${new_flavor}"
  ngcpcfg apply "Switched to ${new_flavor} as key-value server"

  if [[ "${NGCP_TYPE}" == 'carrier' || "${NGCP_TYPE}" == 'sppro' ]]; then
    ngcpcfg push --shared-only
  fi
else
  ngcpcfg apply
fi

# Check if the system is running
if systemctl is-active --quiet "${new_flavor}"; then
    echo "INFO: the switch has been completed and ${new_flavor} is running"
else
    die "The switch has been completed but ${new_flavor} is NOT running. Please check it!"
fi

# Check the status of the replica before exiting
if [[ "${new_flavor}" == 'valkey' ]]; then
  loops=1
  max_loops=120
  while true ; do
    #replica_status=$(keydb-cli info replication |grep slave0 | sed -n 's/.*lag=\([^,]*\).*/\1/p')
    replica_status=$(ngcp-ssh "${NGCP_PEERNAME}" "keydb-cli info replication |grep slave0 | sed -n 's/.*lag=\([^,]*\).*/\1/p'" | tr -d '\r')

    if [[ -z "$replica_status" ]]; then
      die "Could not determine replica lag!"
    fi

    if [[ "$replica_status" -le 1 ]] ; then
      echo -e "INFO: The ${new_flavor} services are in sync."
      echo -e "INFO: You can proceed with the switchover"
      break
    else
      echo -e "INFO: Replica lag equal to ${replica_status}, please wait..."
      loops=$((loops+1))
      if [ "$loops" -ge "$max_loops" ] ; then
        die "Please fix the replication before proceeding with the switchover"
      fi
    fi

    sleep 1
  done
fi

exit 0
