#!/usr/bin/env bash
set -euo pipefail

declare -r BASHUNIT_MIN_BASH_VERSION="3.2"

function _check_bash_version() {
  local current_version
  if [[ -n ${BASHUNIT_TEST_BASH_VERSION:-} ]]; then
    # Checks if BASHUNIT_TEST_BASH_VERSION is set (typically for testing purposes)
    current_version="${BASHUNIT_TEST_BASH_VERSION}"
  elif [[ -n ${BASH_VERSINFO+set} ]]; then
    # Checks if the special Bash array BASH_VERSINFO exists. This array is only defined in Bash.
    current_version="${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}"
  else
    # If not in Bash (e.g., running from Zsh). The pipeline extracts just the major.minor version (e.g., 3.2).
    current_version="$(bash --version | head -n1 | cut -d' ' -f4 | cut -d. -f1,2)"
  fi

  local major minor
  IFS=. read -r major minor _ <<< "$current_version"

  if (( major < 3 )) || { (( major == 3 )) && (( minor < 2 )); }; then
    printf 'Bashunit requires Bash >= %s. Current version: %s\n' "$BASHUNIT_MIN_BASH_VERSION" "$current_version" >&2
    exit 1
  fi
}

_check_bash_version

# shellcheck disable=SC2034
declare -r BASHUNIT_VERSION="0.23.0"

# shellcheck disable=SC2155
declare -r BASHUNIT_ROOT_DIR="$(dirname "${BASH_SOURCE[0]}")"
export BASHUNIT_ROOT_DIR

source "$BASHUNIT_ROOT_DIR/src/dev/debug.sh"
source "$BASHUNIT_ROOT_DIR/src/check_os.sh"
source "$BASHUNIT_ROOT_DIR/src/str.sh"
source "$BASHUNIT_ROOT_DIR/src/globals.sh"
source "$BASHUNIT_ROOT_DIR/src/dependencies.sh"
source "$BASHUNIT_ROOT_DIR/src/io.sh"
source "$BASHUNIT_ROOT_DIR/src/math.sh"
source "$BASHUNIT_ROOT_DIR/src/parallel.sh"
source "$BASHUNIT_ROOT_DIR/src/env.sh"
source "$BASHUNIT_ROOT_DIR/src/clock.sh"
source "$BASHUNIT_ROOT_DIR/src/state.sh"
source "$BASHUNIT_ROOT_DIR/src/colors.sh"
source "$BASHUNIT_ROOT_DIR/src/console_header.sh"
source "$BASHUNIT_ROOT_DIR/src/console_results.sh"
source "$BASHUNIT_ROOT_DIR/src/helpers.sh"
source "$BASHUNIT_ROOT_DIR/src/test_title.sh"
source "$BASHUNIT_ROOT_DIR/src/upgrade.sh"
source "$BASHUNIT_ROOT_DIR/src/assertions.sh"
source "$BASHUNIT_ROOT_DIR/src/reports.sh"
source "$BASHUNIT_ROOT_DIR/src/runner.sh"
source "$BASHUNIT_ROOT_DIR/src/bashunit.sh"
source "$BASHUNIT_ROOT_DIR/src/init.sh"
source "$BASHUNIT_ROOT_DIR/src/main.sh"

_ASSERT_FN=""
_FILTER=""
_RAW_ARGS=()
_ARGS=()
_BENCH_MODE=false

check_os::init
clock::init

# Argument parsing
while [[ $# -gt 0 ]]; do
  case "$1" in
    -a|--assert)
      _ASSERT_FN="$2"
      shift
      ;;
    -f|--filter)
      _FILTER="$2"
      shift
      ;;
    -s|--simple)
      export BASHUNIT_SIMPLE_OUTPUT=true
      ;;
    --detailed)
      export BASHUNIT_SIMPLE_OUTPUT=false
      ;;
    --debug)
      OUTPUT_FILE="${2:-}"
      if [[ -n "$OUTPUT_FILE" ]]; then
        exec > "$OUTPUT_FILE" 2>&1
      fi
      set -x
      ;;
    -b|--bench)
      _BENCH_MODE=true
      export BASHUNIT_BENCH_MODE=true
      source "$BASHUNIT_ROOT_DIR/src/benchmark.sh"
      ;;
    -S|--stop-on-failure)
      export BASHUNIT_STOP_ON_FAILURE=true
      ;;
    -p|--parallel)
      export BASHUNIT_PARALLEL_RUN=true
      ;;
    --no-parallel)
      export BASHUNIT_PARALLEL_RUN=false
      ;;
    -e|--env|--boot)
      # shellcheck disable=SC1090
      source "$2"
      shift
      ;;
    -l|--log-junit)
      export BASHUNIT_LOG_JUNIT="$2"
      shift
      ;;
    -r|--report-html)
      export BASHUNIT_REPORT_HTML="$2"
      shift
      ;;
    --no-output)
      export BASHUNIT_NO_OUTPUT=true
      ;;
    -vvv|--verbose)
      export BASHUNIT_VERBOSE=true
      ;;
    -v|--version)
      console_header::print_version
      trap '' EXIT && exit 0
      ;;
    --upgrade)
      upgrade::upgrade
      trap '' EXIT && exit 0
      ;;
    --init)
      if [[ -n ${2:-} && ${2:0:1} != "-" ]]; then
        init::project "$2"
        shift
      else
        init::project
      fi
      trap '' EXIT && exit 0
      ;;
    -h|--help)
      console_header::print_help
      trap '' EXIT && exit 0
      ;;
    *)
      _RAW_ARGS+=("$1")
      ;;
  esac
  shift
done

# Expand positional arguments after all options have been processed
if [[ ${#_RAW_ARGS[@]} -gt 0 ]]; then
  pattern='*[tT]est.sh'
  [[ "$_BENCH_MODE" == true ]] && pattern='*[bB]ench.sh'
  for arg in "${_RAW_ARGS[@]}"; do
    while IFS= read -r file; do
      _ARGS+=("$file")
    done < <(helper::find_files_recursive "$arg" "$pattern")
  done
fi

# Optional bootstrap
# shellcheck disable=SC1090
[[ -f "${BASHUNIT_BOOTSTRAP:-}" ]] && source "$BASHUNIT_BOOTSTRAP"

if [[ "${BASHUNIT_NO_OUTPUT:-false}" == true ]]; then
  exec >/dev/null 2>&1
fi

set +eu

#################
# Main execution
#################
if [[ -n "$_ASSERT_FN" ]]; then
  main::exec_assert "$_ASSERT_FN" "${_ARGS[@]}"
elif [[ "$_BENCH_MODE" == true ]]; then
  main::exec_benchmarks "$_FILTER" "${_ARGS[@]}"
else
  main::exec_tests "$_FILTER" "${_ARGS[@]}"
fi
