#!/bin/bash
# This file is in the public domain.

# shellcheck disable=SC2317

set -eu

# Replace with 0 for nexus...
USE_FAKEBANK=1
if [ 1 = "$USE_FAKEBANK" ]
then
    ACCOUNT="exchange-account-2"
    WIRE_METHOD="x-taler-bank"
    BANK_FLAGS="-f -d $WIRE_METHOD -u $ACCOUNT"
    BANK_URL="http://localhost:18082/"
    MERCHANT_PAYTO="payto://x-taler-bank/localhost/anastasis?receiver-name=anastasis"
else
    ACCOUNT="exchange-account-1"
    WIRE_METHOD="iban"
    BANK_FLAGS="-ns -d $WIRE_METHOD -u $ACCOUNT"
    BANK_URL="http://localhost:18082/"
    MERCHANT_PAYTO="payto://iban/SANDBOXX/DE648226?receiver-name=anastasis"
fi

# Check we can actually run
echo -n "Testing for jq"
jq -h > /dev/null || exit_skip "jq required"
echo " FOUND"
echo -n "Testing for anastasis-reducer ..."
anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
echo " FOUND"

echo -n "Testing for taler"
taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange required"
taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant required"
echo " FOUND"

echo -n "Testing for taler-wallet-cli"
taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"

echo -n "Testing for anastasis-httpd"
anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"

. setup.sh
# Launch exchange, merchant and bank.
# shellcheck disable=SC2086
setup -c "test_reducer.conf" \
      -emw \
      -r merchant-exchange-default \
      $BANK_FLAGS

# Cleanup to run whenever we exit
function cleanup()
{
    exit_cleanup
    for n in $(jobs -p)
    do
        kill "$n" 2> /dev/null || true
    done
    rm -rf "$CONF" "$WALLET_DB" "$R1FILE" "$R2FILE" "$B1FILE" "$B2FILE" "$TMP_DIR"
    wait
}

function sync_providers() {
  infile=$1
  outfile=$2
  echo "Synchronizing providers"
  # Sync with providers (up to 3 providers aren't synced here)
  for x in 1 2 3; do
    echo "Synchronizing providers (round $x)"
    anastasis-reducer sync_providers < "$infile" > "$outfile" 2> /dev/null || true
    CODE=$(jq -r -e ".code // 0" < "$outfile")
    # ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED
    # FIXME: Temporary workaround for C reducer. See #7227.
    if [ "$CODE" = "8420" ]
    then
      # restore previous non-error state
      cat "$infile" > "$outfile"
      break
    fi
    # ANASTASIS_REDUCER_ACTION_INVALID
    if [ "$CODE" = "8400" ]
    then
      # restore previous non-error state
      cat "$infile" > "$outfile"
      break
    fi
    if [ "$CODE" != "0" ]
    then
      exit_fail "Expected no error or 8420/8400, got $CODE"
    fi
    cat "$outfile" > "$infile"
  done
  echo "Providers synced."
}


CONF_1="test_anastasis_reducer_1.conf"
CONF_2="test_anastasis_reducer_2.conf"
CONF_3="test_anastasis_reducer_3.conf"
CONF_4="test_anastasis_reducer_4.conf"


# Configuration file will be edited, so we create one
# from the template.
CONF="$(mktemp -p "${TMPDIR:-/tmp}" test_reducerXXXXXX.conf)"
cp test_reducer.conf "$CONF"

TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d keys-tmp-XXXXXX)
WALLET_DB=$(mktemp -p "${TMPDIR:-/tmp}" test_reducer_wallet.json-XXXXXX)
B1FILE=$(mktemp -p "${TMPDIR:-/tmp}" test_reducer_stateB1XXXXXX)
B2FILE=$(mktemp -p "${TMPDIR:-/tmp}" test_reducer_stateB2XXXXXX)
R1FILE=$(mktemp -p "${TMPDIR:-/tmp}" test_reducer_stateR1XXXXXX)
R2FILE=$(mktemp -p "${TMPDIR:-/tmp}" test_reducer_stateR2XXXXXX)

# Install cleanup handler (except for kill -9)
trap cleanup EXIT

echo -n "Initialize anastasis database ..."
# Name of the Postgres database we will use for the script.
# Will be dropped, do NOT use anything that might be used
# elsewhere
TARGET_DB_1=$(anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")
TARGET_DB_2=$(anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")
TARGET_DB_3=$(anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")
TARGET_DB_4=$(anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")

dropdb "$TARGET_DB_1" >/dev/null 2>/dev/null || true
createdb "$TARGET_DB_1" || exit_skip "Could not create database $TARGET_DB_1"
anastasis-dbinit -c "$CONF_1" 2> anastasis-dbinit_1.log
dropdb "$TARGET_DB_2" >/dev/null 2>/dev/null || true
createdb "$TARGET_DB_2" || exit_skip "Could not create database $TARGET_DB_2"
anastasis-dbinit -c "$CONF_2" 2> anastasis-dbinit_2.log
dropdb "$TARGET_DB_3" >/dev/null 2>/dev/null || true
createdb "$TARGET_DB_3" || exit_skip "Could not create database $TARGET_DB_3"
anastasis-dbinit -c "$CONF_3" 2> anastasis-dbinit_3.log
dropdb "$TARGET_DB_4" >/dev/null 2>/dev/null || true
createdb "$TARGET_DB_4" || exit_skip "Could not create database $TARGET_DB_4"
anastasis-dbinit -c "$CONF_4" 2> anastasis-dbinit_4.log

echo " OK"

echo -n "Launching anastasis services ..."
PREFIX="" #valgrind
$PREFIX anastasis-httpd -c "$CONF_1" 2> anastasis-httpd_1.log &
$PREFIX anastasis-httpd -c "$CONF_2" 2> anastasis-httpd_2.log &
$PREFIX anastasis-httpd -c "$CONF_3" 2> anastasis-httpd_3.log &
$PREFIX anastasis-httpd -c "$CONF_4" 2> anastasis-httpd_4.log &

echo "OK"

echo -n "Waiting for anastasis services ..."

# Wait for anastasis services to be available
for n in $(seq 1 50)
do
    echo -n "."
    sleep 0.1
    OK=0
   # anastasis_01
    wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
    # anastasis_02
    wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
    # anastasis_03
    wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
    # anastasis_04
    wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
    OK=1
    break
done

if [ 1 != "$OK" ]
then
    exit_skip "Failed to launch anastasis services"
fi
echo "OK"

echo -n "Configuring merchant instance ..."
# Setup merchant

curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"id":"admin","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances

curl -H "Content-Type: application/json" -X POST -d '{"payto_uri":"'"$MERCHANT_PAYTO"'"}' http://localhost:9966/private/accounts

echo " DONE"

echo -n "Running backup logic ...,"
anastasis-reducer -b > "$B1FILE"
echo -n "."
anastasis-reducer -a \
  '{"continent": "Demoworld"}' \
  select_continent < "$B1FILE" > "$B2FILE"
echo -n "."
anastasis-reducer -a \
  '{"country_code": "xx"}' \
  select_country < "$B2FILE" > "$B1FILE"
echo -n "."
anastasis-reducer -a \
  '{"identity_attributes": {
    "full_name": "Max Musterman",
    "sq_number": "4",
    "birthdate": "2000-01-01"}}' \
  enter_user_attributes < "$B1FILE" > "$B2FILE"
cat "$B2FILE" > "$B1FILE"
echo -n ","
sync_providers "$B1FILE" "$B2FILE"
echo -n ","
# "91GPWWR" encodes "Hans"
anastasis-reducer -a \
  '{"authentication_method": {
    "type": "question",
    "instructions": "What is your name?",
    "challenge": "91GPWWR"
    } }' \
  add_authentication < "$B2FILE" > "$B1FILE"
echo -n "."
# "64S36" encodes "123"
anastasis-reducer -a \
  '{"authentication_method": {
    "type": "question",
    "instructions": "How old are you?",
    "challenge": "64S36"
    } }' \
  add_authentication < "$B1FILE" > "$B2FILE"
echo -n "."
# "9NGQ4WR" encodes "Mars"
anastasis-reducer -a \
  '{"authentication_method": {
    "type": "question",
    "instructions": "Where do you live?",
    "challenge": "9NGQ4WR"
    } }' \
  add_authentication < "$B2FILE" > "$B1FILE"
echo -n "."
# Finished adding authentication methods
anastasis-reducer \
    next < "$B1FILE" > "$B2FILE"


echo -n ","
# Finished policy review
anastasis-reducer \
  next < "$B2FILE" > "$B1FILE"
echo -n "."

# Note: 'secret' must here be a Crockford base32-encoded value
anastasis-reducer -a \
  '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \
  enter_secret < "$B1FILE" > "$B2FILE"
mv "$B2FILE" "$B1FILE"
anastasis-reducer next "$B1FILE" "$B2FILE"
echo " OK"


echo -n "Preparing wallet"

EXCHANGE_URL="$(taler-exchange-config -c "$CONF" -s exchange -o BASE_URL)"

rm -f "$WALLET_DB"
taler-wallet-cli --no-throttle --wallet-db="$WALLET_DB" api --expect-success 'withdrawTestBalance' \
  "$(jq -n '
    {
        amount: "TESTKUDOS:100",
        corebankApiBaseUrl: $BANK_URL,
        exchangeBaseUrl: $EXCHANGE_URL
    }' \
    --arg BANK_URL "${BANK_URL}" \
    --arg EXCHANGE_URL "$EXCHANGE_URL"
  )" 2> wallet-withdraw.err > wallet-withdraw.out
taler-wallet-cli \
    --wallet-db="$WALLET_DB" \
    run-until-done \
    2>wallet-withdraw-finish.err \
    >wallet-withdraw-finish.out
echo " OK"

echo -en "Making payments for truth uploads ... "
OBJECT_SIZE=$(jq -r -e '.payments | length' < "$B2FILE")
for ((INDEX=0; INDEX < "$OBJECT_SIZE"; INDEX++))
do
    PAY_URI=$(jq --argjson INDEX $INDEX -r -e '.payments[$INDEX]' < "$B2FILE")
    # run wallet CLI
    echo -n "$INDEX"
    taler-wallet-cli \
        --wallet-db="$WALLET_DB" \
        handle-uri "$PAY_URI" \
        -y \
        2>"wallet-pay-truth-$INDEX.err" \
        >"wallet-pay-truth-$INDEX.out"
    echo -n ", "
done
echo "OK"
echo -e "Running wallet run-until-done..."
taler-wallet-cli \
    --wallet-db="$WALLET_DB" \
    run-until-done \
    2>"wallet-pay-truth-finish-$INDEX.err" \
    >"wallet-pay-truth-finish-$INDEX.out"
echo -e "Payments done"

echo -en "Try to upload again ..."
$PREFIX anastasis-reducer pay "$B2FILE" "$B1FILE"
mv "$B1FILE" "$B2FILE"
echo " OK"

echo -en "Making payments for policy uploads ... "
OBJECT_SIZE=$(jq -r -e '.policy_payment_requests | length' < "$B2FILE")
for ((INDEX=0; INDEX < "$OBJECT_SIZE"; INDEX++))
do
    PAY_URI=$(jq --argjson INDEX $INDEX -r -e '.policy_payment_requests[$INDEX].payto' < "$B2FILE")
    # run wallet CLI
    echo -n "$INDEX"
    taler-wallet-cli \
        --wallet-db="$WALLET_DB" \
        handle-uri "$PAY_URI" \
        -y \
        2>"wallet-pay-policy-$INDEX.err" \
        >"wallet-pay-policy-$INDEX.out"
    echo -n ", "
done
echo " OK"
echo -en "Running wallet run-until-done..."
taler-wallet-cli \
    --wallet-db="$WALLET_DB" \
    run-until-done \
    2>wallet-pay-policy-finish.err \
    >wallet-pay-policy-finish.out
echo -e " payments DONE"

echo -en "Try to upload again ..."
anastasis-reducer \
  pay < "$B2FILE" > "$B1FILE"
echo " OK: Backup finished"
echo -n "Final backup checks ..."
STATE=$(jq -r -e .backup_state < "$B1FILE")
if [ "$STATE" != "BACKUP_FINISHED" ]
then
    exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
fi

jq -r -e .core_secret < "$B1FILE" > /dev/null && exit_fail "'core_secret' was not cleared upon success"

echo " OK"

echo -n "Running recovery basic logic ..."
anastasis-reducer -r > "$R1FILE"
anastasis-reducer -a \
  '{"continent": "Demoworld"}' \
  select_continent < "$R1FILE" > "$R2FILE"
anastasis-reducer -a \
  '{"country_code": "xx"}' \
  select_country < "$R2FILE" > "$R1FILE"
anastasis-reducer -a '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01" }}' enter_user_attributes < "$R1FILE" > "$R2FILE"


STATE=$(jq -r -e .recovery_state < "$R2FILE")
if [ "$STATE" != "SECRET_SELECTING" ]
then
    exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'"
fi
echo " OK"

echo -n "Adding provider (to ensure it is loaded)"
anastasis-reducer -a '{"provider_url" : "http://localhost:8086/" }' add_provider < "$R2FILE" > "$R1FILE"
echo " OK"

echo -n "Selecting secret to recover"
anastasis-reducer -a '{"attribute_mask": 0, "providers" : [ { "version": 1, "url" : "http://localhost:8086/" } ] }' select_version < "$R1FILE" > "$R2FILE"

STATE=$(jq -r -e .recovery_state < "$R2FILE")
if [ "$STATE" != "CHALLENGE_SELECTING" ]
then
    exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'"
fi
echo " OK"

cat "$R2FILE" > "$R1FILE"

sync_providers "$R1FILE" "$R2FILE"

echo -n "Running challenge logic ..."

UUID0=$(jq -r -e .recovery_information.challenges[0].uuid < "$R2FILE")
UUID1=$(jq -r -e .recovery_information.challenges[1].uuid < "$R2FILE")
UUID2=$(jq -r -e .recovery_information.challenges[2].uuid < "$R2FILE")
#UUID0Q=$(jq -r -e .recovery_information.challenges[0].instructions < "$R2FILE")
UUID1Q=$(jq -r -e .recovery_information.challenges[1].instructions < "$R2FILE")
UUID2Q=$(jq -r -e .recovery_information.challenges[2].instructions < "$R2FILE")

if [ "$UUID2Q" = 'How old are you?' ]
then
    AGE_UUID=$UUID2
elif [ "$UUID1Q" = 'How old are you?' ]
then
    AGE_UUID=$UUID1
else
    AGE_UUID=$UUID0
fi

if [ "$UUID2Q" = 'What is your name?' ]
then
    NAME_UUID=$UUID2
elif [ "$UUID1Q" = 'What is your name?' ]
then
    NAME_UUID=$UUID1
else
    NAME_UUID=$UUID0
fi

anastasis-reducer -a \
  "$(jq -n '
    {
        uuid: $UUID
    }' \
    --arg UUID "$NAME_UUID"
  )" \
  select_challenge < "$R2FILE" > "$R1FILE"

anastasis-reducer -a '{"answer": "Hans"}' \
  solve_challenge < "$R1FILE" > "$R2FILE"

anastasis-reducer -a \
  "$(jq -n '
    {
        uuid: $UUID
    }' \
    --arg UUID "$AGE_UUID"
  )" \
  select_challenge < "$R2FILE" > "$R1FILE"

anastasis-reducer -a '{"answer": "123"}' \
  solve_challenge < "$R1FILE" > "$R2FILE"

echo " OK"

echo -n "Checking recovered secret ..."
# finally: check here that we recovered the secret...

STATE=$(jq -r -e .recovery_state < "$R2FILE")
if [ "$STATE" != "RECOVERY_FINISHED" ]
then
    jq -e . "$R2FILE"
    exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'"
fi

SECRET=$(jq -r -e .core_secret.value < "$R2FILE")
if [ "$SECRET" != "VERYHARDT0GVESSSECRET" ]
then
    jq -e . "$R2FILE"
    exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'"
fi

MIME=$(jq -r -e .core_secret.mime < "$R2FILE")
if [ "$MIME" != "text/plain" ]
then
    jq -e . "$R2FILE"
    exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'"
fi

echo " OK"

exit 0
