#!/bin/bash
# This file is part of GNU TALER.
# Copyright (C) 2023 Taler Systems SA
#
# TALER is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 2.1, or (at your option) any later version.
#
# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with
# TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
#
# @author Christian Grothoff
#
#
# Error checking on
set -eu

RESET_DB=0
SKIP_DBINIT=0
DBUSER="taler-merchant-httpd"
CFGFILE="/etc/taler-merchant/taler-merchant.conf"

# Parse command-line options
while getopts 'c:hrsu:' OPTION; do
  case "$OPTION" in
  c)
    CFGFILE="$OPTARG"
    ;;
  h)
    echo 'Supported options:'
    echo "  -c FILENAME  -- use configuration FILENAME (default: $CFGFILE)"
    echo "  -h           -- print this help text"
    echo "  -r           -- reset database (dangerous)"
    echo "  -s           -- skip database initialization"
    echo "  -u USER      -- taler-merchant to be run by USER (default: $DBUSER)"
    exit 0
    ;;
  r)
    RESET_DB="1"
    ;;
  s)
    SKIP_DBINIT="1"
    ;;
  u)
    DBUSER="$OPTARG"
    ;;
  ?)
    echo 'Invalid command-line option; use -h for help.' >&2
    exit 1
    ;;
  esac
done

if ! id postgres >/dev/null; then
  echo "Could not find 'postgres' user. Please install Postgresql first"
  exit 1
fi

if [ "$(id -u)" -ne 0 ]; then
  echo "This script must be run as root"
  exit 1
fi

if [ 0 = "$SKIP_DBINIT" ]; then
  if ! command -v taler-merchant-dbinit >/dev/null 2>&1; then
    echo "Required 'taler-merchant-dbinit' not found in PATH." >&2
    echo "Install package 'taler-merchant' (same suite as libgnunet)." >&2
    exit 1
  fi
  DBINIT=$(command -v taler-merchant-dbinit)
  # Binary may exist but fail to load (e.g. missing libgnunet).
  DBINIT_ERR=$(mktemp)
  if ! "$DBINIT" -v >"$DBINIT_ERR" 2>&1; then
    echo "Required 'taler-merchant-dbinit' is installed ($DBINIT) but failed to run:" >&2
    sed 's/^/  /' "$DBINIT_ERR" >&2 || true
    rm -f "$DBINIT_ERR"
    echo "Common cause: missing or mismatched shared libraries (often libgnunet)." >&2
    echo "Try: ldd \"$DBINIT\" | grep 'not found'" >&2
    echo "Install matching libgnunet packages for this Debian suite, then re-run." >&2
    exit 1
  fi
  rm -f "$DBINIT_ERR"
fi

if ! id "$DBUSER" >/dev/null; then
  echo "Could not find '$DBUSER' user. Please set it up first"
  exit 1
fi

# Resolve and validate the target before making any database changes.  The
# provisioning commands below administer the default local PostgreSQL cluster.
DBPATH=$(taler-merchant-config \
  -c "$CFGFILE" \
  -s merchantdb-postgres \
  -o CONFIG)

if [[ ! "$DBPATH" =~ ^postgres:///([a-zA-Z0-9_.-]+)$ ]]; then
  echo "Database provisioning requires postgres:///NAME (letters, digits, _, . or -)." >&2
  echo "For other connections, provision the database separately and run taler-merchant-dbinit -c $CFGFILE as the database owner." >&2
  exit 1
fi
DBNAME="${BASH_REMATCH[1]}"

# Query the catalogs explicitly: failure to connect is not evidence that a
# role or database is absent.  -X ignores psqlrc, and SQL errors must be fatal.
ROLE_EXISTS=$(sudo -i -u postgres psql -X -At \
  --set=ON_ERROR_STOP=1 --dbname=postgres --set=dbuser="$DBUSER" <<'EOF'
SELECT EXISTS (SELECT FROM pg_roles WHERE rolname = :'dbuser');
EOF
)
if [ "$ROLE_EXISTS" = f ]; then
  echo "Creating database user $DBUSER." >&2
  sudo -i -u postgres createuser -- "$DBUSER"
elif [ "$ROLE_EXISTS" != t ]; then
  echo "Could not determine whether database user $DBUSER exists." >&2
  exit 1
fi

# Required during migrations; this does not restore other privileges that an
# administrator may have revoked from the database owner.
sudo -i -u postgres psql -X --set=ON_ERROR_STOP=1 \
  --dbname=postgres --set=dbuser="$DBUSER" <<'EOF'
GRANT SET ON PARAMETER session_replication_role TO :"dbuser";
EOF

DB_EXISTS=$(sudo -i -u postgres psql -X -At \
  --set=ON_ERROR_STOP=1 --dbname=postgres --set=dbname="$DBNAME" <<'EOF'
SELECT EXISTS (SELECT FROM pg_database WHERE datname = :'dbname');
EOF
)
case "$DB_EXISTS" in
  t)
    if [ 1 = "$RESET_DB" ]; then
      echo "Deleting existing database $DBNAME." >&2
      sudo -i -u postgres dropdb -- "$DBNAME"
      DB_EXISTS=f
    else
      echo "Database '$DBNAME' already exists, continuing anyway." >&2
    fi
    ;;
  f) ;;
  *)
    echo "Could not determine whether database $DBNAME exists." >&2
    exit 1
    ;;
esac

if [ "$DB_EXISTS" = f ]; then
  echo "Creating database $DBNAME." >&2
  sudo -i -u postgres createdb -O "$DBUSER" -- "$DBNAME"
fi

if [ 0 = "$SKIP_DBINIT" ]; then
  echo "Initializing or upgrading database $DBNAME." >&2
  if ! sudo -u "$DBUSER" "$DBINIT" -c "$CFGFILE"; then
    echo "Failed to initialize database schema." >&2
    echo "Command was: sudo -u $DBUSER $DBINIT -c $CFGFILE" >&2
    echo "Re-run that command for full errors; ensure Postgres is up and CONFIG is correct." >&2
    exit 1
  fi
fi

echo "Database configuration finished." 1>&2

exit 0
