#!/usr/bin/env bash
# akema-shop.sh: produce a small shop's orders and payments to a local Kafka,
# for the Akema tutorial (https://akema.io/docs/tutorial/).
#
# It writes JSON messages to two topics, keyed by order ID, through the console
# producer inside the Kafka container, so you need nothing but Docker and bash.
# Stop it with Ctrl-C.
#
# Settings, as environment variables:
#   KAFKA_CONTAINER  the Kafka container's name      (default akema-kafka)
#   RATE             orders per second               (default 5)
#   INCIDENT_AFTER   seconds before PayPal fails     (default 120)

set -u
KAFKA_CONTAINER=${KAFKA_CONTAINER:-akema-kafka}
RATE=${RATE:-5}
INCIDENT_AFTER=${INCIDENT_AFTER:-120}

producer() {
  docker exec -i "$KAFKA_CONTAINER" /opt/kafka/bin/kafka-console-producer.sh \
    --bootstrap-server localhost:9092 --topic "$1" \
    --property parse.key=true --property parse.headers=true \
    --property null.marker=NULL 2>/dev/null
}

if ! docker exec "$KAFKA_CONTAINER" true 2>/dev/null; then
  echo "Can't reach a container named $KAFKA_CONTAINER. Start Kafka first." >&2
  exit 1
fi

exec 3> >(producer shop-orders)
exec 4> >(producer shop-payments)
trap 'exec 3>&- 4>&-; echo; echo "Stopped after $n orders."; exit 0' INT TERM

COUNTRIES=(US US US US GB GB DE DE FR NL CA AU)
METHODS=(card card card card card paypal paypal apple_pay)
TAB=$'\t'
pause=$(awk -v r="$RATE" 'BEGIN { printf "%.3f", 1 / r }')
start=$SECONDS
n=0
order=100000

echo "Producing to shop-orders and shop-payments in $KAFKA_CONTAINER at $RATE orders/s. Ctrl-C stops."
while :; do
  n=$((n + 1))
  order=$((order + 1))
  id="ORD-$order"
  num=$((RANDOM % 2000 + 1))
  cust=$(printf 'C%05d' "$num")
  email=$(printf 'c%05d@example.com' "$num")
  country=${COUNTRIES[RANDOM % ${#COUNTRIES[@]}]}
  items=$((RANDOM % 4 + 1))
  cents=$((items * (RANDOM % 6000 + 1500)))
  if ((RANDOM % 60 == 0)); then cents=$((cents + 150000)); fi
  total=$(printf '%d.%02d' $((cents / 100)) $((cents % 100)))

  # About 1 order in 100 comes from a load-test client that sends no key
  if ((RANDOM % 100 == 0)); then
    key=NULL; source=load-test
  else
    key=$id
    if ((RANDOM % 3 == 0)); then source=mobile; else source=web; fi
  fi

  printf 'source:%s%s%s%s{"order_id":"%s","customer_id":"%s","email":"%s","country":"%s","items":%d,"total_usd":%s}\n' \
    "$source" "$TAB" "$key" "$TAB" "$id" "$cust" "$email" "$country" "$items" "$total" >&3

  # About 2 orders in 100 never get a payment
  if ((RANDOM % 50 != 0)); then
    method=${METHODS[RANDOM % ${#METHODS[@]}]}
    status=captured; reason=null
    if ((SECONDS - start >= INCIDENT_AFTER)) && [ "$method" = paypal ] && ((RANDOM % 10 < 4)); then
      status=declined; reason='"provider_timeout"'
    elif ((RANDOM % 50 == 0)); then
      status=declined; reason='"insufficient_funds"'
    fi
    # About 1 payment in 100 charges a different amount from the order
    paid=$cents
    if ((RANDOM % 100 == 0)); then paid=$((cents + 1000)); fi
    amount=$(printf '%d.%02d' $((paid / 100)) $((paid % 100)))
    printf 'source:payments%s%s%s{"order_id":"%s","payment_id":"PAY-%d","method":"%s","amount_usd":%s,"status":"%s","decline_reason":%s}\n' \
      "$TAB" "$id" "$TAB" "$id" "$order" "$method" "$amount" "$status" "$reason" >&4
  fi

  if ((n % 100 == 0)); then echo "$n orders"; fi
  sleep "$pause"
done
