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

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

make -C "$ROOT" -s >/dev/null

mkdir -p "$ROOT/build"

BIN="$ROOT/flowlog"
REG="$ROOT/tests/regressions"
PROG="$REG/and_par_user_pred.pl"

# Force RC loading off for deterministic test runs regardless of user shell env.
export FLOWLOG_RC_FILE="$ROOT/build/.flowlog_test_rc_disabled"
rm -f "$FLOWLOG_RC_FILE" >/dev/null 2>&1 || true

fail() {
  echo "fail: $1" >&2
  exit 1
}

run_out() {
  "$BIN" --threads 2 -f -g "$1" "$PROG" | tr -d '\r'
}

run_prog_out() {
  local prog="$1"
  local goal="$2"
  "$BIN" --threads 2 -f -g "$goal" "$prog" | tr -d '\r'
}

time_real() {
  if command -v /usr/bin/time >/dev/null 2>&1; then
    (/usr/bin/time -p "$@" >/dev/null) 2>&1 | awk '/^real[[:space:]]+/{print $2; exit}'
    return 0
  fi

  # Fallback: coarse (seconds).
  local t0 t1
  t0="$(date +%s)"
  "$@" >/dev/null
  t1="$(date +%s)"
  echo "$((t1 - t0))"
}

out="$("$BIN" --threads 1 --engine interp -g '\+ (true -> fail).' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "parse_not_if_then"
fi

out="$("$BIN" --threads 1 -f "$REG/init_entry_halt.pl" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "init_ok" ]]; then
  echo "--- expected ---" >&2
  echo "init_ok" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "init_entry_halt"
fi

out="$("$BIN" --threads 1 -f -g 'ok.' "$REG/init_before_goal.pl" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "init_before_goal"
fi

out="$(run_out 'doubleComplex(1,O).')"
out="${out%$'\n'}"
if [[ "$out" != "   O = 4." ]]; then
  echo "--- expected ---" >&2
  echo "   O = 4." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "and_par_user_pred_output"
fi

seq="$(time_real "$BIN" --threads 2 --engine interp -f -g 'set_prolog_flag(flowlog_parallel_and,off), doubleComplex(1,O).' "$PROG")"
par="$(time_real "$BIN" --threads 2 --engine interp -f -g 'set_prolog_flag(flowlog_parallel_and,on), doubleComplex(1,O).' "$PROG")"

awk -v seq="$seq" -v par="$par" 'BEGIN {
  if (seq + 0.0 <= 0.0 || par + 0.0 <= 0.0) exit 1;
  # Expect a noticeable wall-time reduction when AND-par is enabled.
  if ((seq / par) < 1.3) exit 2;
}' || fail "and_par_user_pred_timing (seq=$seq par=$par)"

seq_wam="$(time_real "$BIN" --threads 2 --engine wam -f -g 'current_prolog_flag(flowlog_engine_active,wam), set_prolog_flag(flowlog_parallel_and,off), doubleComplex(1,O).' "$PROG")"
par_wam="$(time_real "$BIN" --threads 2 --engine wam -f -g 'current_prolog_flag(flowlog_engine_active,wam), set_prolog_flag(flowlog_parallel_and,on), doubleComplex(1,O).' "$PROG")"

awk -v seq="$seq_wam" -v par="$par_wam" 'BEGIN {
  if (seq + 0.0 <= 0.0 || par + 0.0 <= 0.0) exit 1;
  # Expect a noticeable wall-time reduction when AND-par is enabled.
  if ((seq / par) < 1.3) exit 2;
}' || fail "and_par_user_pred_timing_wam (seq=$seq_wam par=$par_wam)"

bash "$ROOT/tests/regressions/run_sigint_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_siginfo_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_siginfo_key_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_ctrld_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_read_loop_flush_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_stream_all_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_solutions_cache_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_false_query_output.sh" >/dev/null
bash "$ROOT/tests/regressions/run_double_quotes_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_double_quotes_default.sh" >/dev/null
bash "$ROOT/tests/regressions/run_file_to_chars.sh" >/dev/null
bash "$ROOT/tests/regressions/run_file_to_bytes.sh" >/dev/null
bash "$ROOT/tests/run_packed_strings_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_bracket_consult_repl.sh" >/dev/null
bash "$ROOT/tests/regressions/run_orpar_nesting.sh" >/dev/null
bash "$ROOT/tests/regressions/run_orpar_nesting_wam.sh" >/dev/null
bash "$ROOT/tests/regressions/run_inference_limit.sh" >/dev/null
bash "$ROOT/tests/regressions/run_cstack_limit.sh" >/dev/null
bash "$ROOT/tests/regressions/run_depth_limits.sh" >/dev/null
bash "$ROOT/tests/regressions/run_parse_error_diagnostics.sh" >/dev/null
bash "$ROOT/tests/regressions/run_read_term_error_diagnostics.sh" >/dev/null
bash "$ROOT/tests/regressions/run_wamvm_trace_ports.sh" >/dev/null
bash "$ROOT/tests/regressions/run_cli_argv_split.sh" >/dev/null

SELECT_CYCLIC_PROG="$REG/select_cyclic.pl"
for engine in interp wam wamvm; do
  out="$("$BIN" --threads 2 --engine "$engine" -f -g 'ok.' "$SELECT_CYCLIC_PROG" | tr -d '\r')"
  out="${out%$'\n'}"
  if [[ "$out" != "   true." ]]; then
    echo "--- expected ---" >&2
    echo "   true." >&2
    echo "--- got ---" >&2
    printf '%s\n' "$out" >&2
    fail "select_cyclic_$engine"
  fi
done

LOOP_PROG="$REG/infinite_loop.pl"
for engine in interp wam wamvm; do
  out="$(FLOWLOG_CSTACK_LIMIT=65536 "$BIN" --threads 2 --engine "$engine" -f -g 'ok.' "$LOOP_PROG" | tr -d '\r')"
  out="${out%$'\n'}"
  if [[ "$out" != "   true." ]]; then
    echo "--- expected ---" >&2
    echo "   true." >&2
    echo "--- got ---" >&2
    printf '%s\n' "$out" >&2
    fail "infinite_loop_$engine"
  fi
done

BOUND_PROG="$REG/wamvm_tail_bound_scope.pl"
out="$("$BIN" --threads 2 --engine wamvm -f -g 'ok.' "$BOUND_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_tail_bound_scope"
fi

out="$("$BIN" --threads 2 -f -g 'ok.' "$REG/length_long.pl" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "length_long"
fi

BIGINT_PROG="$REG/bigint_arith.pl"
for engine in interp wam wamvm; do
  out="$("$BIN" --threads 2 --engine "$engine" -f -g 'ok.' "$BIGINT_PROG" | tr -d '\r')"
  out="${out%$'\n'}"
  if [[ "$out" != "   true." ]]; then
    echo "--- expected ---" >&2
    echo "   true." >&2
    echo "--- got ---" >&2
    printf '%s\n' "$out" >&2
    fail "bigint_arith_$engine"
  fi
done

WAM_PROG="$REG/wam_engine_smoke.pl"
out="$(run_prog_out "$WAM_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_ok"
fi

out="$(run_prog_out "$WAM_PROG" 'fact(5,F).')"
out="${out%$'\n'}"
if [[ "$out" != "   F = 120." ]]; then
  echo "--- expected ---" >&2
  echo "   F = 120." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_fact"
fi

out="$(run_prog_out "$WAM_PROG" 'count(C).')"
out="${out%$'\n'}"
if [[ "$out" != "   C = 2." ]]; then
  echo "--- expected ---" >&2
  echo "   C = 2." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_findall_length"
fi

WAM_TABLING_PROG="$REG/wam_tabling.pl"
out="$(run_prog_out "$WAM_TABLING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_tabling"
fi

FINDALL_PROG="$REG/findall_non_ground.pl"
out="$(run_prog_out "$FINDALL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "findall_non_ground_fallback"
fi

FINDALL_VARS_PROG="$REG/findall_vars.pl"
out="$(run_prog_out "$FINDALL_VARS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "findall_vars_wam"
fi

WAM_CTRL_PROG="$REG/wam_control_constructs.pl"
out="$(run_prog_out "$WAM_CTRL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_control_constructs"
fi

WAM_TERM_OPS_PROG="$REG/wam_term_ops.pl"
out="$(run_prog_out "$WAM_TERM_OPS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_term_ops"
fi

WAM_SORTING_PROG="$REG/wam_sorting.pl"
out="$(run_prog_out "$WAM_SORTING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_sorting"
fi

WAM_SUB_STRING_PROG="$REG/wam_sub_string.pl"
out="$(run_prog_out "$WAM_SUB_STRING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_sub_string"
fi

WAM_EXTRA_BUILTINS_PROG="$REG/wam_extra_builtins.pl"
out="$(run_prog_out "$WAM_EXTRA_BUILTINS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_extra_builtins"
fi

WAM_EXC_PROG="$REG/wam_exceptions.pl"
out="$(run_prog_out "$WAM_EXC_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_exceptions"
fi

WAM_COPY_VARS_PROG="$REG/wam_copy_vars.pl"
out="$(run_prog_out "$WAM_COPY_VARS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_copy_vars"
fi

WAM_CONV_PROG="$REG/wam_conversions.pl"
out="$(run_prog_out "$WAM_CONV_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_conversions"
fi

WAM_BAGOF_PROG="$REG/wam_bagof_setof.pl"
out="$(run_prog_out "$WAM_BAGOF_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_bagof_setof"
fi

WAM_OPS_PROG="$REG/wam_ops.pl"
out="$(run_prog_out "$WAM_OPS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_ops"
fi

WAM_FLAGS_PROG="$REG/wam_flags.pl"
out="$(run_prog_out "$WAM_FLAGS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_flags"
fi

WAMVM_FLAGS_PROG="$REG/wamvm_flags.pl"
out="$(run_prog_out "$WAMVM_FLAGS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_flags"
fi

WAMVM_VM_SMOKE_PROG="$REG/wamvm_vm_smoke.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f -g 'findall(X,p(X),Xs).' "$WAMVM_VM_SMOKE_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   Xs = [1,2]." ]]; then
  echo "--- expected ---" >&2
  echo "   Xs = [1,2]." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_vm_smoke"
fi

WAMVM_FINDALL_VAR_GOAL_PROG="$REG/wamvm_findall_var_goal_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f -g 'ok.' "$WAMVM_FINDALL_VAR_GOAL_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_findall_var_goal_vm"
fi

WAMVM_SELECT1_VM_PROG="$REG/wamvm_select1_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f -g 'ok.' "$WAMVM_SELECT1_VM_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_select1_vm"
fi

WAMVM_TERM_OPS_VM_PROG="$REG/wamvm_term_ops_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f -g 'ok.' "$WAMVM_TERM_OPS_VM_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_term_ops_vm"
fi

WAMVM_EXCEPTIONS_VM_PROG="$REG/wamvm_exceptions_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f -g 'ok.' "$WAMVM_EXCEPTIONS_VM_PROG" | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_exceptions_vm"
fi

# VM-only compatibility pass: ensure common WAM-era ISO tests run entirely in WAMVM mode.
for f in "$REG"/wam_*.pl; do
  base="$(basename "$f")"
  case "$base" in
    wam_flags.pl|wam_tabling.pl) continue ;;
  esac
  out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm --parallel-profile off -f -g 'ok.' "$f" | tr -d '\r')"
  out="${out%$'\n'}"
  if [[ "$out" != "   true." ]]; then
    echo "--- expected ---" >&2
    echo "   true." >&2
    echo "--- got ---" >&2
    printf '%s\n' "$out" >&2
    fail "wamvm_vm_only_${base%.pl}"
  fi
done

WAM_REFL_PROG="$REG/wam_reflective.pl"
out="$(run_prog_out "$WAM_REFL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_reflective"
fi

WAM_PROP_PROG="$REG/wam_properties.pl"
out="$(run_prog_out "$WAM_PROP_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_properties"
fi

WAM_DYN_DB_PROG="$REG/wam_dynamic_db.pl"
out="$(run_prog_out "$WAM_DYN_DB_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_dynamic_db"
fi

WAM_IO_PROG="$REG/wam_stream_io.pl"
out="$(run_prog_out "$WAM_IO_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "   true." ]]; then
  echo "--- expected ---" >&2
  echo "   true." >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_stream_io"
fi

echo "ok"
