#!/usr/bin/env bash
_modules=('system' 'bat' 'bat_config' 'bat_wrapper' 'bat_wrapper_function' 'tool')
_modules_consented=()

set -o pipefail

export LC_ALL=C
export LANG=C

BAT="bat"
if ! command -v bat &>/dev/null; then
	if command -v batcat &> /dev/null; then
		BAT="batcat"
	else
		tput setaf 1
		printf "%s\n%s\n" \
			"Unable to find a bat executable on your PATH." \
			"Please ensure that 'bat' exists and is not named something else."
		tput sgr0
		exit 1
	fi
fi

# -----------------------------------------------------------------------------
# Modules:
# -----------------------------------------------------------------------------

_bat_:description() {
	_collects "Version information for 'bat'."
	_collects "Custom syntaxes and themes for 'bat'."
}

_bat_config_:description() {
	_collects "The environment variables used by 'bat'."
	_collects "The 'bat' configuration file."
}

_bat_wrapper_:description() {
	_collects "Any wrapper script used by 'bat'."
}

_bat_wrapper_function_:description() {
	_collects "The wrapper function surrounding 'bat' (if applicable)."
}

_system_:description() {
	_collects "Operating system name."
	_collects "Operating system version."
}

_tool_:description() {
	_collects "Version information for 'less'."
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_bat_:run() {
	_out "$BAT" --version
	_out env | grep '^BAT_\|^PAGER='

	local cache_dir="$(bat --cache-dir)"
	if [[ -f "${cache_dir}/syntaxes.bin" ]]; then
		_print_command "$BAT" "--list-languages"
		echo "Found custom syntax set."
	fi

	if [[ -f "${cache_dir}/themes.bin" ]]; then
		_print_command "$BAT" "--list-themes"
		echo "Found custom theme set."
	fi
}

_bat_config_:run() {
	if [[ -f "$("$BAT" --config-file)" ]]; then
		_out_fence cat "$("$BAT" --config-file)"
	fi
}

_bat_wrapper_:run() {
	_bat_wrapper_:detect_wrapper() {
		local bat="$1"
		if file "$(which "${bat}")" | grep "text executable" &> /dev/null; then
			_out_fence cat "$(which "${bat}")"
			return
		fi

		printf "\nNo wrapper script for '%s'.\n" "${bat}"
	}

	_bat_wrapper_:detect_wrapper bat
	if [[ "$BAT" != "bat" ]]; then
		_bat_wrapper_:detect_wrapper "$BAT"
	fi
}

_bat_wrapper_function_:run() {
	_bat_wrapper_function_:detect_wrapper() {
		local command="$1"
		case "$("$SHELL" --version | head -n 1)" in
			*fish*)
				if "$SHELL" --login -i -c "type ${command}" 2>&1 | grep 'function' &> /dev/null; then
					_out_fence "$SHELL" --login -i -c "functions ${command}"
					return
				fi ;;

			*bash* | *zsh*)
				local type="$("$SHELL" --login -i -c "type ${command}" 2>&1)"
				if grep 'function' <<< "$type" &> /dev/null; then
					_out_fence "$SHELL" --login -i -c "declare -f ${command}"
					return
				elif grep 'alias' <<< "$type" &> /dev/null; then
					_out_fence "$SHELL" --login -i -c "type ${command}"
					return
				fi ;;

			*)
				echo "Unable to determine if a wrapper function for '${command}' is set."
				return ;;
		esac
		printf "\nNo wrapper function for '%s'.\n" "${command}"
	}

	_bat_wrapper_function_:detect_wrapper bat
	_bat_wrapper_function_:detect_wrapper cat
	if [[ "$BAT" != "bat" ]]; then
		_bat_wrapper_function_:detect_wrapper "$BAT"
	fi
}

_system_:run() {
	_out uname -srm

	if command -v "sw_vers" &> /dev/null; then _out sw_vers; fi
	if command -v "lsb_release" &> /dev/null; then _out lsb_release -a; fi
}

_tool_:run() {
	_out less --version | head -n1
}

# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------

_print_command() {
	printf '\n**$' 1>&2
	printf ' %s' "$@" 1>&2
	printf '**\n' 1>&2
}

_out() {
	_print_command "$@"
	"$@" 2>&1 | sed 's/$/  /'
}

_out_fence() {
	_print_command "$@"
	printf '```\n' 1>&2
	"$@" 2>&1
	printf '```\n' 1>&2
}

_tput() {
	tput "$@" 1>&2 2> /dev/null
}

_collects() {
	printf " - %s\n" "$1" 1>&2
}

_ask_module() {
	_tput clear
	_tput cup 0 0

	cat 1>&2 << EOF
--------------------------------------------------------------------------------
This script runs some harmless commands to collect information about your
system and bat configuration. It will give you a small preview of the commands
that will be run, and ask consent before running them. Once completed, it will
output a small report that you can review and copy into the issue description.
--------------------------------------------------------------------------------
EOF

	# Print description.
	_tput setaf 3
	printf "The following data will be collected:\n" 1>&2
	_tput sgr0
	"_$1_:description"
	_tput sgr0

	# Print preview.
	_tput setaf 3
	printf "\nThe following commands will be run:\n" 1>&2
	_tput sgr0
	declare -f "_$1_:run" \
		| sed 's/^ *//; s/;$//' \
		| grep '^_out[^ ]* ' \
		| sed 's/^_out[^ ]* //' \
		| sed "s/\"\$BAT\"/$BAT/" 1>&2

	# Prompt
	printf "\n" 1>&2
	local response
	while true; do
		_tput cup "$(($( tput lines || echo 22) - 2))"
		_tput el
		read -er -p "Collect $(sed 's/_/ /' <<< "$1") data? [Y/n] " response
		case "$response" in
			Y | y | yes | '') return 0 ;;
			N | n | no) return 1 ;;
			*) continue ;;
		esac
	done
}

_run_module() {
	local module="$1"
	printf "%s\n%s\n" "$module" "$(printf "%${#module}s" | tr ' ' '-')"
	"_$1_:run"
}

# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------

# Tell the user if their executable isn't named "bat".
if [[ "$BAT" != "bat" ]] && [[ "$1" != '-y' ]]; then
	trap '_tput rmcup; exit 1' INT
	_tput smcup
	_tput clear
	_tput cup 0 0
	_tput setaf 1
	printf "The %s executable on your system is named '%s'.\n%s\n" "bat" "$BAT" \
		"If your issue is related to installation, please check that this isn't the issue."
	_tput sgr0
	printf "Press any key to continue...\n"
	read -rsn1
	_tput rmcup
fi

# Ask for consent.
if [[ "$1" == '-y' ]]; then
	_modules_consented=("${_modules[@]}")
else
	trap '_tput rmcup; exit 1' INT
	_tput smcup
	for _module in "${_modules[@]}"; do
		if _ask_module "$_module"; then
			_modules_consented+=("$_module")
		fi
	done
	_tput rmcup
fi

# Collect information.
for _module in "${_modules_consented[@]}"; do
	_run_module "$_module" 2>&1
	printf "\n"
done