#!/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
source "${BASH_TOOLBOX:?}"/yes-no.sh

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

: ${SELF:="$(basename $0)"}

LOCAL_RUN_DIR="${REPOSITORY_ROOT:?}"/local-run
SELF_SCRIPTS="${LOCAL_RUN_DIR:?}"/scripts
source "${SELF_SCRIPTS:?}"/constants.sh
: ${SCENARIO_NAME:="${DEFAULT_SCENARIO_NAME:?}"}

# ===== HELPER FUNCTIONS =====

description() {
	cat <<EOF
${Bold}Runs a Prose Pod API locally.${Bold_Off}

By default, this script will use the latest released images$(format_secondary '[^1]') but you can override
this behavior by using $(format_arg --api=DOCKER_TAG) and/or $(format_arg --server=DOCKER_TAG).

$(format_secondary "[^1]: At the time the current commit was made (i.e. it won’t use $(format_code "${LATEST_DOCKER_TAG:?}"), for")
$(format_secondary '      backwards compatibility reasons).')
EOF
}

usage() {
	cat <<EOF
$(format_title 'Usage:')
  You want to run the latest released versions:
    $(format_command "${SELF%' --'}")
  You want to run with the latest patches (latest commits, unreleased):
    $(format_command "${SELF:?}") $(format_arg '--api=edge')
  You want to run the image you built locally (e.g. for integration tests):
    $(format_command "${SELF:?}") $(format_arg "--api=${LOCAL_DOCKER_TAG:?}")

$(format_title 'Options:')
  $(format_subtitle 'Version options:')
    $(format_flag --api=DOCKER_TAG)
      Choose which version of the Prose Pod API to run (default: $(format_code "${PROSE_POD_API_IMAGE_TAG:?}")).
    $(format_flag --server=DOCKER_TAG)
      Choose which version of the Prose Pod Server to run (default: $(format_code "${PROSE_POD_SERVER_IMAGE_TAG:?}")).
    $(format_flag --dashboard=DOCKER_TAG)
      Choose which version of the Prose Pod Dashboard to run (default: $(format_code "${PROSE_POD_DASHBOARD_IMAGE_TAG:?}")).
      $(format_secondary "The Prose Pod Dashboard doesn’t run by default, you must edit $(format_hyperlink 'compose.yaml' file:"${REPOSITORY_ROOT:?}"/local-run/compose.yaml).")

  $(format_subtitle 'Run options:')
    $(format_flag --scenario=SCENARIO_NAME)
      Choose what scenario to use for this run (default: $(format_code "${SCENARIO_NAME:?}")).
      $(format_secondary 'This can be used to start a Prose Pod API with pre-inserted data.')
    $(format_flag --ephemeral)
      Choose what scenario to use for this run (default: $(format_code "${SCENARIO_NAME:?}")).
      $(format_secondary 'This can be used to start a Prose Pod API with pre-inserted data.')
    $(format_flag --ephemeral-name=EPHEMERAL_SCENARIO_NAME)
      Choose a custom name for the ephemeral scenario (default: scenario name
      with an auto-generated incrementing suffix).
      $(format_secondary 'This can be used when you need a predictable name.')
    $(format_flag --detach)
      Run containers in the background. Check logs with $(format_code 'task local:logs').
      $(format_secondary "Same as $(format_code docker compose up --detach).")
    $(format_flag --compose-profile=*)
      Enable one or more $(format_hyperlink 'Compose profiles' 'https://docs.docker.com/compose/how-tos/profiles/').
      $(format_secondary "See $(format_hyperlink 'compose.yaml' file:"${REPOSITORY_ROOT:?}"/local-run/compose.yaml) to find the possible profiles.")
    $(format_flag +dashboard)
      Also start a Prose Pod Dashboard.
      $(format_secondary "Equivalent to $(format_code --compose-profile=dashboard).")
    $(format_flag +telemetry)
      Also run the optional $(format_code otel-collector) service for telemetry collection.
      $(format_secondary "Equivalent to $(format_code --compose-profile=telemetry).")

  $(format_subtitle 'Miscellaneous options:')
    $(format_flag --help)
      Explains what the command does and how to use it.
    $(format_flag --dry-run)
      Do a dry run (i.e. print what would be executed instead of running it).
    $(format_flag --debug)
      Log debug messages when running the script.
      $(format_secondary 'This won’t change the log levels of the API and server yet, but we’ll
      implement that someday.')
    $(format_flag --trace)
      Log tracing messages when running the script.
      $(format_secondary 'This won’t change the log levels of the API and server yet, but we’ll
      implement that someday.')
EOF
}

help() {
	printf "$(description)\n"
	echo ''
	printf "$(usage)\n"
	exit 0
}

scenario-does-not-exist() {
	cat <<EOF
The scenario $(format_code "${SCENARIO_NAME:?}") doesn’t exist.

You can create it by running $(format_code "task local:scenarios:create -- '${SCENARIO_NAME:?}'"),
but you could also derive it from an existing one using $(format_code '--based-on=') in addition.
EOF
}

# ===== ARGUMENT PARSING =====

for arg in "$@"; do
	case $arg in
		--api=*)
			PROSE_POD_API_IMAGE_TAG="${arg#'--api='}"
			# Regenerate image name.
			unset PROSE_POD_API_IMAGE && source "${SCRIPTS_ROOT:?}"/image-names.sh
			;;
		--server=*)
			PROSE_POD_SERVER_IMAGE_TAG="${arg#'--server='}"
			# Regenerate image name.
			unset PROSE_POD_SERVER_IMAGE && source "${SCRIPTS_ROOT:?}"/image-names.sh
			;;
		--dashboard=*)
			PROSE_POD_DASHBOARD_IMAGE_TAG="${arg#'--dashboard='}"
			# Regenerate image name.
			unset PROSE_POD_DASHBOARD_IMAGE && source "${SCRIPTS_ROOT:?}"/image-names.sh
			;;
		--scenario=*) SCENARIO_NAME="${arg#'--scenario='}" ;;
		--ephemeral) EPHEMERAL=1 ;;
		--ephemeral-name=*) EPHEMERAL_SCENARIO_NAME="${arg#'--ephemeral-name='}" ;;
		--detach) DETACH=1 ;;
		--compose-profile=*) COMPOSE_PROFILES+="${COMPOSE_PROFILES:+,}""${arg#'--compose-profile='}" ;;
		+dashboard) COMPOSE_PROFILES+="${COMPOSE_PROFILES:+,}"dashboard ;;
		+telemetry) COMPOSE_PROFILES+="${COMPOSE_PROFILES:+,}"telemetry ;;
		--help) help ;;
		--dry-run) export DRY_RUN=1 ;;
		--debug) export LOG_DEBUG=1 ;;
		--trace) export LOG_TRACE=1 ;;
		*) error "Unknown argument: $(format_code $arg)."; info "$(usage)"; die ;;
	esac
done

# Recompute log levels.
source "${BASH_TOOLBOX:?}"/log.sh

if (( $EPHEMERAL )); then
	BASE_SCENARIO_NAME="${SCENARIO_NAME:?}"
	export SCENARIO_NAME="${EPHEMERAL_SCENARIO_NAME:-"${SCENARIO_NAME:?}-$(date +%s)"}"
fi

# Compute dynamic variables.
source "${SELF_SCRIPTS:?}"/scenario-files.sh

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

if (( $EPHEMERAL )); then
	edo task local:scenarios:create -- "${SCENARIO_NAME:?}" ${BASE_SCENARIO_NAME:+--based-on="${BASE_SCENARIO_NAME:?}"}
	echo "${SCENARIO_NAME:?}" >> "${EPHEMERAL_SCENARIOS_FILE:?}"
elif [ -f "${SCENARIO_DIR:?}"/NO_RUN ] && ! (( $FORCE_RUN )); then
	die "Cannot run the $(format_code "${SCENARIO_NAME:?}") scenario. You must copy it ($(format_code "task local:scenarios:create -- NEW_NAME --based-on='${SCENARIO_NAME:?}'")) or run it with $(format_code --ephemeral)."
fi

if ! [ -d "${SCENARIO_DIR:?}" ]; then
	edo log_as_warn_ scenario-does-not-exist
	edo if_yes "Would you like to create a new one based on the default and continue?" "n" task local:scenarios:create -- "${SCENARIO_NAME:?}"
fi

# Recompute dynamic variables and fallback to using files
# from the `default` scenario if they don’t exist.
source "${SELF_SCRIPTS:?}"/scenario-files-fallback.sh
source "${SCENARIO_CONSTANTS_FILE:?}"

info "Running scenario: $(format_code "${SCENARIO_NAME:?}")"
info 'Selected versions:'
info "- API: $(format_code "${PROSE_POD_API_IMAGE:?}")"
info "- Server: $(format_code "${PROSE_POD_SERVER_IMAGE:?}")"
case ",${COMPOSE_PROFILES-}," in
  *",dashboard,"*) info "- Dashboard: $(format_code "${PROSE_POD_DASHBOARD_IMAGE:?}")" ;;
esac

# NOTE: The database doesn't exist yet in integration tests.
[ -f "${DATABASE_PATH:?}" ] || echo '' > "${DATABASE_PATH:?}"
[ -f "${MAILPIT_DATABASE_PATH:?}" ] || echo '' > "${MAILPIT_DATABASE_PATH:?}"

source "${SELF_SCRIPTS:?}"/compose-exports.sh
if [[ "${PROSE_POD_API_IMAGE_TAG:?}" =~ ^(edge|latest)$ ]]; then
	edo docker compose -f "${COMPOSE_FILE:?}" pull
fi

if [ -n "${COMPOSE_PROFILES-}" ]; then
	trace "Running Compose with profiles $(format_code "${COMPOSE_PROFILES:?}")"
fi
export COMPOSE_PROFILES
edo docker compose -f "${COMPOSE_FILE:?}" up --force-recreate --remove-orphans ${DETACH+--detach}

if (( $EPHEMERAL )) && ! (( $DETACH )); then
	log_as_trace_ task local:scenarios:delete -y -- "${SCENARIO_NAME:?}"
fi
