#!/usr/bin/env bash

# Configure the script to exit when a command fails.
set -e

: ${SCRIPTS_ROOT:="$(dirname $0)"/../../scripts}
export SCRIPTS_ROOT
source "${SCRIPTS_ROOT:?}"/util.sh

# ===== CONSTANTS =====

: ${REPOSITORY_ROOT:="${PROSE_POD_API_DIR:?}"}
LOCAL_RUN_DIR="${REPOSITORY_ROOT:?}"/local-run
DEMO_DATA_DIR="${LOCAL_RUN_DIR:?}"/demo-data
: ${SCENARIOS_DIR:="${LOCAL_RUN_DIR:?}"/scenarios}
: ${LOCAL_RUN_SCRIPTS_DIR:="${LOCAL_RUN_DIR:?}"/scripts}

: ${SCENARIO_NAME:=demo}
: ${DOMAIN:=example.org}

source "${DEMO_DATA_DIR:?}/constants.sh"
source "${LOCAL_RUN_SCRIPTS_DIR:?}/scenario-files.sh"

# ===== MAIN LOGIC =====

# Create demo scenario.
if [ -d "${SCENARIO_DIR:?}" ]; then
	edo task local:scenarios:reset -y -- "${SCENARIO_NAME:?}"
else
	edo task local:scenarios:create -- "${SCENARIO_NAME:?}" --based-on=fresh
fi

echo '' > "${SCENARIO_DIR:?}"/NO_RUN
echo '' > "${SCENARIO_DIR:?}"/NO_DELETE

if [[ "$*" == *'--pod-address-dynamic'* ]]; then
	ADDRESS_CFG="address = { domain = \"prose.${DOMAIN:?}\" }"
else
	ADDRESS_CFG='address = { ipv4 = "172.20.0.40", ipv6 = "2001:db8::40" }'
fi

mkdir -p "$(dirname "${PROSE_CONFIG_FILE:?}")"
cat <<EOF > "${PROSE_CONFIG_FILE:?}"
# Prose Pod API
# REST API for administrating a Prose Pod
# Configuration file
# Template: https://github.com/prose-im/prose-pod-system/blob/master/templates/prose.toml
# All keys: https://github.com/prose-im/prose-pod-api/blob/master/src/service/src/features/app_config/mod.rs

[branding]
company_name = "Prose (demo)"

[server]
domain = "${DOMAIN:?}"

[pod]
${ADDRESS_CFG:?}

[dashboard]
url = "http://localhost:8081"

[notifiers.email]
smtp_host = "mailpit"
smtp_encrypt = false
EOF

mkdir -p "$(dirname "${COREDNS_COREFILE:?}")"
cat <<EOF > "${COREDNS_COREFILE:?}"
.:53 {
  file /etc/coredns/zones.db
  errors         # show errors
  log            # enable query logs
}
EOF

mkdir -p "$(dirname "${DNS_ZONE_FILE:?}")"
cat <<EOF > "${DNS_ZONE_FILE:?}"
@ IN SOA ns.${DOMAIN:?}. admin.${DOMAIN:?}. (
  2024101701 ; serial
  3600       ; refresh (1 hour)
  600        ; retry (10 minutes)
  86400      ; expire (1 day)
  3600       ; minimum (1 hour)
)
admin.prose.${DOMAIN:?}. 3600 IN CNAME prose.${DOMAIN:?}.
EOF

if [[ "$*" == *'--pod-address-dynamic'* ]]; then
	cat <<EOF >> "${DNS_ZONE_FILE:?}"
_xmpp-client._tcp.${DOMAIN:?}. 3600 IN SRV 0 5 5222 prose.${DOMAIN:?}.
_xmpp-server._tcp.${DOMAIN:?}. 3600 IN SRV 0 5 5269 prose.${DOMAIN:?}.
EOF
else
	cat <<EOF >> "${DNS_ZONE_FILE:?}"
prose.${DOMAIN:?}. 600 IN A 172.20.0.40
prose.${DOMAIN:?}. 600 IN AAAA 2001:db8::40
_xmpp-client._tcp.${DOMAIN:?}. 3600 IN SRV 0 5 5222 prose.${DOMAIN:?}.
_xmpp-server._tcp.${DOMAIN:?}. 3600 IN SRV 0 5 5269 prose.${DOMAIN:?}.
EOF
fi

# Run the API.
start() {
	export ENV_FILE="${REPOSITORY_ROOT:?}"/local-run/scripts/make-demo-scenario.env
	# NOTE: We need to run `local` so we can auto-accept invitations.
	edo FORCE_RUN=1 task local:run -- --api=local --scenario="${SCENARIO_NAME:?}" --detach
	for _ in $(seq 1 5); do
		trace 'Waiting a second…'
		sleep 1
		local error="$(edo xh :8080/health -q)" && return 0 || :
	done
	error 'API still unreachable after 5s:'
	error "${error-}"
	return 1
}
info 'Starting the API…'
start

info 'Create first admin account'
xh PUT :8080/v1/init/first-account -q \
	username="${MEMBER_USERNAMES[0]}" \
	password="${MEMBER_PASSWORDS[0]}" \
	nickname="${MEMBER_NICKNAMES[0]}"

info 'Initialize workspace'
xh PUT :8080/v1/workspace -q \
	name="${WORKSPACE_NAME:?}"

log-in-member-n() {
	local n="${1:?}"
	local res="$(xh POST :8080/v1/login -a "${MEMBER_JIDS[n]}:${MEMBER_PASSWORDS[n]}" -b)"
	if [ $? -eq 0 ]; then
		MEMBER_TOKENS[n]="$(jq -r '.token' <<< "${res:?}")"
	else
		debug 'Retrying (debug)…'
		log_as_debug_ xh POST :8080/v1/login -a "${MEMBER_JIDS[n]}:${MEMBER_PASSWORDS[n]}" -b
		return 1
	fi
}

info 'Log first admin in'
log-in-member-n 0
ADMIN_TOKEN="${MEMBER_TOKENS[0]}"

info 'Set Workspace icon'
xh PUT :8080/v1/workspace/icon -q \
	Content-Type:application/octet-stream \
	-A bearer -a "${ADMIN_TOKEN:?}" \
	< "${AVATARS_DIR:?}"/prose.png

update-member-n() {
	local n="${1:?}"

	local nickname="${MEMBER_NICKNAMES[n]}"
	if [ -n "${nickname?}" ]; then
		info "Set ${MEMBER_JIDS[n]}’s nickname"
		edo xh PUT :8080/v1/members/"${MEMBER_JIDS[n]}"/nickname -q \
			--raw "\"${nickname:?}\"" \
			-A bearer -a "${MEMBER_TOKENS[n]}"
	else
		warn "Member ${n:?} (${MEMBER_JIDS[n]}) has no nickname"
	fi

	local avatar="${MEMBER_AVATARS[n]}"
	if [ -n "${avatar?}" ]; then
		info "Set ${MEMBER_JIDS[n]}’s avatar"
		edo xh PUT :8080/v1/members/"${MEMBER_JIDS[n]}"/avatar -q \
			--raw "\"${avatar:?}\"" \
			-A bearer -a "${MEMBER_TOKENS[n]}"
		local avatar_set="$(xh GET :8080/v1/members/"${MEMBER_JIDS[n]}" -b \
			-A bearer -a "${MEMBER_TOKENS[n]}" | jq -r '.avatar.base64')"
		if [ "$avatar_set" != "$avatar" ]; then
			info "avatar_set: $avatar_set"
			info "avatar: $avatar"
			die
		fi
		local avatar_set="$(xh GET :8080/v1/enrich-members?jids="${MEMBER_JIDS[n]}" -b \
			-A bearer -a "${MEMBER_TOKENS[n]}" | jq -r ".[\"${MEMBER_JIDS[n]}\"].avatar.base64")"
		if [ "$avatar_set" != "$avatar" ]; then
			info "avatar_set2: $avatar_set"
			info "avatar: $avatar"
			die
		fi
	else
		warn "Member ${n:?} (${MEMBER_JIDS[n]}) has no avatar"
	fi

	local email="${MEMBER_EMAILS[n]}"
	if [ -z "${email-}" ]; then
		warn "Member ${n:?} (${MEMBER_JIDS[n]}) has no email address"
	fi
}

add-member-n() {
	local n="${1:?}"
	info "Add member ${MEMBER_JIDS[n]} (via auto-accepted invitation)"
	xh POST :8080/v1/invitations -q \
		auto_accept==true \
		username="${MEMBER_USERNAMES[n]}" \
		pre_assigned_role="${MEMBER_ROLES[n]}" \
		channel=email \
		email_address="${MEMBER_EMAILS[n]}" \
		-A bearer -a "${ADMIN_TOKEN:?}"

	info "Log ${MEMBER_JIDS[n]} in"
	log-in-member-n "${n:?}"

	update-member-n "${n:?}"
}

update-member-n 0

for n in $(seq 1 $(( ${#MEMBER_USERNAMES[@]} - 1 ))); do
	add-member-n "${n:?}"
done

# NOTE: When creating a user, Prosody adds it to the desired
#   groups using an event handler. However, this action takes
#   some time (see the benchmarks) therefore we wait a bit to
#   ensure it has properly finished.
# TODO: Is this really necessary? Does Prosody finish
#   this task on a graceful shutdown?
info Waiting
sleep 3

success 'Done'

edo task local:stop

info Ensuring database is not corrupted…
edo log_as_trace_ sqlite3 "${DATABASE_PATH:?}" 'PRAGMA integrity_check;'
