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

index=0

init_suite() {
  count=0
  failures=0
  skipped=0
  suitetest_exec_time=0
  name=""
  _buffer=""
  _buffer_log=""
}

init_suite

host() {
  local hostname="${HOST:-}"
  [[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
  [[ -z "$hostname" ]] && hostname="$(uname -n)"
  [[ -z "$hostname" ]] && hostname="$(hostname -f)"

  echo "$hostname"
}

header() {
  timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
  printf "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">
  <properties>" "${class}" "${count}" "${failures}" "${skipped}" "${suitetest_exec_time}" "${timestamp}" "$(host)"
  while IFS= read -r line; do
    name="$(echo "$line" | cut -d'=' -f1)"
    value="$(echo "$line" | cut -d'=' -f2-)"
    printf "\n    <property name=\"%s\" value=\"%s\"/>" "${name}" "${value}"
  done < <(env | grep "^BATS_")
  printf "\n  </properties>"
}

footer() {
  printf "  <system-out></system-out>\n"
  printf "  <system-err></system-err>\n"
  printf "</testsuite>\n"
}

pass() {
  printf "\n    <testcase classname=\"%s\" name=\"%s\" time=\"%s\"/>" "${class}" "${name}" "${test_exec_time}"
}

fail() {
  printf "\n    <testcase classname=\"%s\" name=\"%s\" time=\"%s\">
        <failure type=\"failure\"><![CDATA[" "${class}" "${name}" "${test_exec_time}"

  echo -n "${1}"

  printf "]]></failure>
    </testcase>\n"
}

skip() {
  # shellcheck disable=SC2183
  printf "\n    <testcase classname=\"%s\" name=\"%s\" time=\"%s\">
        <skipped>%s</skipped>
    </testcase>\n" "${class}" "${name}" "${test_exec_time}" "$1"
}

buffer() {
  _buffer="${_buffer}$("$@")"
}

flush() {
  echo "${_buffer}"
  _buffer=""
}

log() {
  _buffer_log="${_buffer_log}
$1"
}

flush_log() {
  if [[ -n "${_buffer_log}" ]]; then
    buffer fail "${_buffer_log}"
    _buffer_log=""
  fi
}

finish_suite() {
  [[ ${count} -gt 0 ]] && {
    (
      flush_log
      header
      flush
      footer
    ) >"$(output_path)"
  }
  init_suite
}

output_path() {
  path="TestReport-${class}.xml"
  if [[ -n "${BATS_REPORT_OUTPUT_PATH:-}" ]]; then
    path="${BATS_REPORT_OUTPUT_PATH%/}/${path}"
  fi
  echo "$path"
}

trap finish_suite EXIT

while IFS= read -r line; do
  case "$line" in
  "suite "*)
    flush_log
    finish_suite
    suite_expr="suite (.*)"
    if [[ "$line" =~ $suite_expr ]]; then
      class="${BASH_REMATCH[1]}"
    fi
    ;;
  "begin "*)
    flush_log
    ((index += 1))
    name="${line#* $index }"
    ;;
  "ok "*)
    echo "$line"
    ((count += 1))
    expr_ok="ok $index .* in ([0-9]+)sec"
    expr_skip="ok $index .* # skip[ ]?(.*)"
    if [[ "$line" =~ $expr_skip ]]; then
      ((skipped += 1))
      test_exec_time=0
      buffer skip "${BASH_REMATCH[1]}"
    elif [[ "$line" =~ $expr_ok ]]; then
      test_exec_time="${BASH_REMATCH[1]}"
      suitetest_exec_time=$((suitetest_exec_time + test_exec_time))
      buffer pass
    else
      log "Wrong output format: ${line}"
      ((failures += 1))
    fi
    ;;
  "not ok "*)
    echo "$line"
    ((count += 1))
    ((failures += 1))
    expr_notok="not ok $index .* in ([0-9]+)sec"
    if [[ "$line" =~ $expr_notok ]]; then
      test_exec_time="${BASH_REMATCH[1]}"
      suitetest_exec_time=$((suitetest_exec_time + test_exec_time))
    fi
    ;;
  "# "*)
    log "${line:2}"
    ;;
  *)
    echo "$line"
    ;;
  esac
done
