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

set -eu
#set -x

# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
    echo " SKIP: $1"
    exit 77
}

# Exit, with error message (hard failure)
function exit_fail() {
    echo " FAIL: $1"
    exit 1
}

# Cleanup to run whenever we exit
function cleanup()
{
    for n in $(jobs -p)
    do
        kill "$n" 2> /dev/null || true
    done
    rm -rf "$CONF" "$BANK_CONF" "$R1FILE" "$R2FILE" "$B1FILE" "$B2FILE"
    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
      cp "$infile" "$outfile"
      break
    fi
    # ANASTASIS_REDUCER_ACTION_INVALID
    if [ "$CODE" = "8400" ]
    then
      # restore previous non-error state
      cp "$infile" "$outfile"
      break
    fi
    if [ "$CODE" != "0" ]
    then
      exit_fail "Expected no error or 8420/8400, got $CODE"
    fi
    cp "$outfile" "$infile"
  done
  echo "Providers synced."
}


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


# Transfer funds from the customer's account to the Anastasis account.
# $1 = amount ($CURRENCY:X.Y), $2 = subject.
function wire_transfer_to_anastasis() {
  echo -n "Initiating wire transfer ..."
  curl -s -X POST \
       --user "${DEBIT_USERNAME}:${DEBIT_PASSWORD}" \
       -H "Content-Type: application/json" \
       -d "{\"payto_uri\":\"${PAYTO_CREDIT}&message=$(echo -n "$2" | jq -sRr @uri)&amount=$1\"}" \
       "${BANK_URL}accounts/${DEBIT_USERNAME}/transactions" \
       > libeufin-transfer.out
  echo " OK"
}

# Merely a debug utility: show what Anastasis sees on the credit account.
function see_anastasis_transactions() {
  curl -s --user "${CREDIT_USERNAME}:${CREDIT_PASSWORD}" \
       "${BANK_URL}accounts/${CREDIT_USERNAME}/taler-revenue/history?limit=5" | jq
}

# Configuration file will be edited, so we create one
# from the template.
CONF=$(mktemp test_free_reducerXXXXXX.conf)
cp test_free_reducer.conf "$CONF"
BANK_CONF=$(mktemp test_iban_bankXXXXXX.conf)



B1FILE=$(mktemp test_reducer_stateB1XXXXXX)
B2FILE=$(mktemp test_reducer_stateB2XXXXXX)
R1FILE=$(mktemp test_reducer_stateR1XXXXXX)
R2FILE=$(mktemp test_reducer_stateR2XXXXXX)

export CONF
export B2FILE
export B1FILE
export R2FILE
export R1FILE

echo -n "Testing for libeufin-bank"
libeufin-bank --help > /dev/null || exit_skip "libeufin-bank required"
echo " FOUND"

echo -n "Testing for curl"
curl --version > /dev/null || exit_skip "curl required"
echo " FOUND"

# 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 "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=$(anastasis-config -c "$CONF" -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")

dropdb "$TARGET_DB" >/dev/null 2>/dev/null || true
createdb "$TARGET_DB" || exit_skip "Could not create database $TARGET_DB"
anastasis-dbinit -c "$CONF" 2> anastasis-dbinit.log

echo " OK"


CURRENCY="EUR"
BANK_DB=anastasisbankcheck
BANK_PORT=8092
BANK_URL="http://localhost:${BANK_PORT}/"
# Both IBANs have to pass the IBAN checksum, libeufin-bank rejects the rest.
export IBAN_CREDIT="DE89370400440532013000"
export IBAN_DEBIT="FR1420041010050500013M02606"
PERSON_CREDIT_NAME="Person Credit"
CREDIT_USERNAME=anastasis-credit-user
CREDIT_PASSWORD=anastasis-credit-password
DEBIT_USERNAME=anastasis-debit-user
DEBIT_PASSWORD=anastasis-debit-password
PAYTO_CREDIT="payto://iban/${IBAN_CREDIT}?receiver-name=Person+Credit"
PAYTO_DEBIT="payto://iban/${IBAN_DEBIT}?receiver-name=Person+Debit"

echo -n "Configuring bank ..."
cat > "$BANK_CONF" <<EOF
# Generated by test_iban.sh
[libeufin-bank]
CURRENCY = ${CURRENCY}
BASE_URL = ${BANK_URL}
SERVE = tcp
PORT = ${BANK_PORT}
WIRE_TYPE = iban
IBAN_PAYTO_BIC = SANDBOXX
DEFAULT_CUSTOMER_DEBT_LIMIT = ${CURRENCY}:200
DEFAULT_ADMIN_DEBT_LIMIT = ${CURRENCY}:2000
ALLOW_REGISTRATION = YES
# keep the test fast
PWD_HASH_CONFIG = { "cost": 4 }
# the Anastasis IBAN helper authenticates with HTTP basic auth
PWD_AUTH_COMPAT = yes

[libeufin-bankdb-postgres]
CONFIG = postgres:///${BANK_DB}
EOF
dropdb "$BANK_DB" >/dev/null 2>/dev/null || true
createdb "$BANK_DB" || exit_skip "Could not create database $BANK_DB"
libeufin-bank dbinit -c "$BANK_CONF" &> libeufin-bank-dbinit.log
echo " OK"

echo -n "Starting bank ..."
libeufin-bank serve -c "$BANK_CONF" &> libeufin-bank.log &
OK=0
for n in $(seq 1 50)
do
    sleep 0.2
    wget --tries=1 --timeout=1 "${BANK_URL}config" \
         -o /dev/null -O /dev/null >/dev/null || continue
    OK=1
    break
done
if [ 1 != $OK ]
then
    exit_skip "Could not launch libeufin-bank"
fi
echo " OK"

echo -n "Preparing accounts ..."
libeufin-bank create-account -c "$BANK_CONF" \
              -u "$CREDIT_USERNAME" -p "$CREDIT_PASSWORD" \
              --name "$PERSON_CREDIT_NAME" \
              --payto_uri "$PAYTO_CREDIT" > /dev/null
libeufin-bank create-account -c "$BANK_CONF" \
              -u "$DEBIT_USERNAME" -p "$DEBIT_PASSWORD" \
              --name "Person Debit" \
              --payto_uri "$PAYTO_DEBIT" > /dev/null
# the customer has to be able to send money it does not have
libeufin-bank edit-account -c "$BANK_CONF" \
              --debit_threshold="${CURRENCY}:1000" \
              "$DEBIT_USERNAME" > /dev/null
echo " OK"

echo -n "Configuring Anastasis IBAN account ..."
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o CURRENCY \
                 -V "${CURRENCY}"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o CREDIT_IBAN \
                 -V "${IBAN_CREDIT}"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o BUSINESS_NAME \
                 -V "${PERSON_CREDIT_NAME}"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o WIRE_GATEWAY_URL \
                 -V "${BANK_URL}accounts/${CREDIT_USERNAME}/taler-revenue/"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o WIRE_GATEWAY_AUTH_METHOD \
                 -V "basic"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o USERNAME \
                 -V "${CREDIT_USERNAME}"
anastasis-config -c "$CONF" \
                 -s authorization-iban \
                 -o PASSWORD \
                 -V "${CREDIT_PASSWORD}"
echo " OK"

echo -n "Launching Anastasis service ..."
PREFIX="" #valgrind
$PREFIX anastasis-httpd -c "$CONF" -L INFO 2> anastasis-httpd_1.log &
echo " OK"

echo -n "Waiting for Anastasis service ..."
# Wait for Anastasis service 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
    OK=1
    break
done
if [ 1 != $OK ]
then
    exit_skip "Failed to launch Anastasis service"
fi
echo "OK"

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" 2>> test_reducer.err
echo -n "."

anastasis-reducer -a \
  '{"identity_attributes": {
    "full_name": "Max Musterman",
    "sq_number": "4",
    "birthdate": "2000-01-01"}}' \
  enter_user_attributes < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
echo -n ","
cat "$B2FILE" > "$B1FILE"
sync_providers "$B1FILE" "$B2FILE"
echo -n ","
BASEIBAN=$(echo -n $IBAN_DEBIT | gnunet-base32)
anastasis-reducer -a \
  "$(jq -n '{ authentication_method: {
    type: "iban",
    instructions: "Send me your money!",
    challenge: $CHALLENGE
    } }' \
   --arg CHALLENGE "$BASEIBAN"
  )" \
  add_authentication < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
echo -n "."

# "91GPWWR" encodes "Hans"
anastasis-reducer -a \
  '{"authentication_method": {
    "type": "question",
    "instructions": "What is your name?",
    "challenge": "91GPWWR"
    } }' \
  add_authentication < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
echo -n "."

mv "$B2FILE" "$B1FILE"

# Finished adding authentication methods
anastasis-reducer \
    next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err

echo -n ","
# Finished policy review
anastasis-reducer \
  next < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
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" 2>> test_reducer.err
mv "$B2FILE" "$B1FILE"
anastasis-reducer next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
echo " OK"

echo -n "Final backup checks ..."
STATE=$(jq -r -e .backup_state < "$B2FILE")
if [ "$STATE" != "BACKUP_FINISHED" ]
then
    exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
fi
jq -r -e .core_secret < "$B2FILE" > /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",
    "currencies":["TESTKUDOS"]}' \
  select_country < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
anastasis-reducer -a '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01" }}' enter_user_attributes < "$R1FILE" > "$R2FILE" 2>> test_reducer.err


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" 2>> test_reducer.err

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"

cp "$R2FILE" "$R1FILE"
sync_providers "$R1FILE" "$R2FILE"

echo -n "Running challenge selection logic ..."

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

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

echo "OK"
echo -n "Solving first challenge ..."
anastasis-reducer -a \
  "$(jq -n '
    {
        uuid: $UUID
    }' \
    --arg UUID "$NAME_UUID"
  )" \
  select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err

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

echo "OK"
echo -n "Solving IBAN challenge ..."

anastasis-reducer -a \
  "$(jq -n '
    {
        uuid: $UUID
    }' \
    --arg UUID "$IBAN_UUID"
  )" \
  select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
echo "OK"


METHOD=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".state < "$R1FILE")
if [ "$METHOD" != "iban-instructions" ]
then
    exit_fail "Expected method to be 'iban-instructions', got ${METHOD}"
fi

ACC=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".target_iban < "$R1FILE")
if [ "$ACC" != "${IBAN_CREDIT}" ]
then
    exit_fail "Expected account to be ${IBAN_CREDIT}, got ${ACC}"
fi

anastasis-reducer \
  back < "$R1FILE" > "$R2FILE" 2>> test_reducer.err


AMOUNT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".challenge_amount < "$R1FILE")
SUBJECT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".wire_transfer_subject < "$R1FILE")

echo -n "Performing authorization wire transfer ${SUBJECT} ..."
wire_transfer_to_anastasis "${AMOUNT}" "${SUBJECT}"

echo " OK"

echo -n "Triggering inbound check ..."
anastasis-helper-authorization-iban -c "$CONF" -t -L INFO
echo " OK"

# Now we should get the secret...
echo -n "Polling for recovery ..."
anastasis-reducer poll -L INFO < "$R2FILE" > "$R1FILE"
echo " OK"

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

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

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

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

echo " OK"

exit 0
