#!/usr/bin/env bash
# Comprehensive tests for the debug script
# Tests: noop, toggleDebug, expect.error, setTrap, onReturn, onError, stackTrace

#export PS4='\e[90m+${LINENO} in ${#BASH_SOURCE[@]}>${FUNCNAME[0]}:${BASH_SOURCE[@]##*/} \e[0m'
#set -x
TEST_CATEGORY=core

level=$1
if [ -z "$level" ]; then
  level=1
else
  shift
fi
echo "starting: ${BASH_SOURCE[@]##*/} <LOG_LEVEL=$level>"

source this
source test.suite
source log

log.level $level

# CRITICAL: Disable STEP_DEBUG to prevent test blocking
export STEP_DEBUG=OFF

# Save original state
ORIGINAL_LOG_LEVEL=$LOG_LEVEL
ORIGINAL_STEP_DEBUG=$STEP_DEBUG
ORIGINAL_EXPECTED_RETURN_VALUE=$EXPECTED_RETURN_VALUE

# Source debug after disabling STEP_DEBUG
source debug

# Cleanup function
cleanup_debug_test() {
  export LOG_LEVEL=$ORIGINAL_LOG_LEVEL
  export STEP_DEBUG=OFF
  export EXPECTED_RETURN_VALUE=$ORIGINAL_EXPECTED_RETURN_VALUE
  # Remove traps to prevent interference
  trap - DEBUG ERR RETURN EXIT 2>/dev/null
}

# Ensure STEP_DEBUG stays off
export STEP_DEBUG=OFF

# ============================================================================
# T1: Test noop returns 0
# ============================================================================
test.case - "T1: noop returns 0" \
  noop

if [ "$RETURN_VALUE" -eq 0 ]; then
  create.result 0 "noop returns 0"
else
  create.result 1 "noop returned $RETURN_VALUE"
fi
expect 0 "noop returns 0" "noop function returns 0"

# ============================================================================
# T2: Test noop does nothing (no side effects)
# ============================================================================
BEFORE_VAR="unchanged"
test.case - "T2: noop has no side effects" \
  noop

if [ "$BEFORE_VAR" = "unchanged" ]; then
  create.result 0 "no side effects"
else
  create.result 1 "side effect detected"
fi
expect 0 "no side effects" "noop has no side effects"

# ============================================================================
# T3: Test toggleDebug changes LOG_LEVEL from low to high
# ============================================================================
export LOG_LEVEL=3
export STEP_DEBUG=OFF

test.case - "T3: toggleDebug increases LOG_LEVEL from 3" \
  toggleDebug

if [ "$LOG_LEVEL" -eq 5 ]; then
  create.result 0 "level increased to 5"
else
  create.result 1 "LOG_LEVEL=$LOG_LEVEL"
fi
expect 0 "level increased to 5" "toggleDebug increases level"

# Reset
export STEP_DEBUG=OFF

# ============================================================================
# T4: Test toggleDebug changes LOG_LEVEL from high to low
# ============================================================================
export LOG_LEVEL=5
export STEP_DEBUG=OFF

test.case - "T4: toggleDebug decreases LOG_LEVEL from 5" \
  toggleDebug

if [ "$LOG_LEVEL" -eq 3 ]; then
  create.result 0 "level decreased to 3"
else
  create.result 1 "LOG_LEVEL=$LOG_LEVEL"
fi
expect 0 "level decreased to 3" "toggleDebug decreases level"

# Reset
export STEP_DEBUG=OFF
export LOG_LEVEL=$level

# ============================================================================
# T5: Test expect.error sets EXPECTED_RETURN_VALUE
# ============================================================================
export EXPECTED_RETURN_VALUE=0

test.case - "T5: expect.error sets EXPECTED_RETURN_VALUE" \
  expect.error 42

if [ "$EXPECTED_RETURN_VALUE" -eq 42 ]; then
  create.result 0 "EXPECTED_RETURN_VALUE=42"
else
  create.result 1 "EXPECTED_RETURN_VALUE=$EXPECTED_RETURN_VALUE"
fi
expect 0 "EXPECTED_RETURN_VALUE=42" "expect.error sets value"

# ============================================================================
# T6: Test expect.error rejects non-numeric input
# ============================================================================
export EXPECTED_RETURN_VALUE=99

test.case - "T6: expect.error rejects non-numeric" \
  expect.error "notanumber"

# Should remain unchanged since "notanumber" is invalid
if [ "$EXPECTED_RETURN_VALUE" -eq 99 ]; then
  create.result 0 "rejected non-numeric"
else
  create.result 1 "accepted non-numeric: $EXPECTED_RETURN_VALUE"
fi
expect 0 "rejected non-numeric" "expect.error rejects non-numeric"

# ============================================================================
# T7: Test setTrap sets ERR trap
# ============================================================================
# Clear traps first
trap - ERR 2>/dev/null

test.case - "T7: setTrap sets ERR trap" \
  setTrap

ERR_TRAP=$(trap -p ERR)
if echo "$ERR_TRAP" | grep -q "onError"; then
  create.result 0 "ERR trap set"
else
  create.result 1 "ERR trap: $ERR_TRAP"
fi
expect 0 "ERR trap set" "setTrap sets ERR trap"

# ============================================================================
# T8: Test setTrap sets RETURN trap
# ============================================================================
RETURN_TRAP=$(trap -p RETURN)
if echo "$RETURN_TRAP" | grep -q "onReturn"; then
  create.result 0 "RETURN trap set"
else
  create.result 1 "RETURN trap: $RETURN_TRAP"
fi
expect 0 "RETURN trap set" "setTrap sets RETURN trap"

# ============================================================================
# T9: Test setTrap sets EXIT trap
# ============================================================================
EXIT_TRAP=$(trap -p EXIT)
if echo "$EXIT_TRAP" | grep -q "onExit"; then
  create.result 0 "EXIT trap set"
else
  create.result 1 "EXIT trap: $EXIT_TRAP"
fi
expect 0 "EXIT trap set" "setTrap sets EXIT trap"

# Clear traps after testing
trap - DEBUG ERR RETURN EXIT 2>/dev/null

# ============================================================================
# T10: Test step function exists
# ============================================================================
test.case - "T10: step function exists" \
  echo "checking step"

if [ "$(type -t step)" = "function" ]; then
  create.result 0 "step is function"
else
  create.result 1 "step type: $(type -t step)"
fi
expect 0 "step is function" "step function exists"

# ============================================================================
# T11: Test stackTrace function exists
# ============================================================================
test.case - "T11: stackTrace function exists" \
  echo "checking stackTrace"

if [ "$(type -t stackTrace)" = "function" ]; then
  create.result 0 "stackTrace is function"
else
  create.result 1 "stackTrace type: $(type -t stackTrace)"
fi
expect 0 "stackTrace is function" "stackTrace function exists"

# ============================================================================
# T12: Test onError function exists
# ============================================================================
test.case - "T12: onError function exists" \
  echo "checking onError"

if [ "$(type -t onError)" = "function" ]; then
  create.result 0 "onError is function"
else
  create.result 1 "onError type: $(type -t onError)"
fi
expect 0 "onError is function" "onError function exists"

# ============================================================================
# T13: Test onReturn function exists
# ============================================================================
test.case - "T13: onReturn function exists" \
  echo "checking onReturn"

if [ "$(type -t onReturn)" = "function" ]; then
  create.result 0 "onReturn is function"
else
  create.result 1 "onReturn type: $(type -t onReturn)"
fi
expect 0 "onReturn is function" "onReturn function exists"

# ============================================================================
# T14: Test onExit function exists
# ============================================================================
test.case - "T14: onExit function exists" \
  echo "checking onExit"

if [ "$(type -t onExit)" = "function" ]; then
  create.result 0 "onExit is function"
else
  create.result 1 "onExit type: $(type -t onExit)"
fi
expect 0 "onExit is function" "onExit function exists"

# ============================================================================
# T15: Test STEP_DEBUG can be toggled
# ============================================================================
export STEP_DEBUG=OFF

test.case - "T15: STEP_DEBUG can be set to ON" \
  echo "testing STEP_DEBUG"

export STEP_DEBUG=ON
if [ "$STEP_DEBUG" = "ON" ]; then
  create.result 0 "STEP_DEBUG=ON"
else
  create.result 1 "STEP_DEBUG=$STEP_DEBUG"
fi
expect 0 "STEP_DEBUG=ON" "STEP_DEBUG can be toggled"

# Reset immediately
export STEP_DEBUG=OFF

# ============================================================================
# T16: Test debug.v returns version (uses info.log, needs level > 3)
# ============================================================================
SAVED_LEVEL=$LOG_LEVEL
SAVED_DEVICE=$LOG_DEVICE
export LOG_LEVEL=4
# Use a temp file so output is always captured regardless of TTY permissions
T16_TMPFILE="/tmp/test.debug.t16.$$"
> "$T16_TMPFILE"
export LOG_DEVICE="$T16_TMPFILE"

test.case - "T16: debug.v returns version" \
  echo "checking version"

debug.v 2>&1
if grep -q "version" "$T16_TMPFILE"; then
  create.result 0 "version returned"
else
  create.result 1 "output: $(cat "$T16_TMPFILE")"
fi
expect 0 "version returned" "debug.v returns version string"

# Restore
rm -f "$T16_TMPFILE" 2>/dev/null
export LOG_LEVEL=$SAVED_LEVEL
export LOG_DEVICE=$SAVED_DEVICE

# ============================================================================
# T17: Test expect.error with 0
# ============================================================================
export EXPECTED_RETURN_VALUE=1

test.case - "T17: expect.error accepts 0" \
  expect.error 0

if [ "$EXPECTED_RETURN_VALUE" -eq 0 ]; then
  create.result 0 "accepts 0"
else
  create.result 1 "EXPECTED_RETURN_VALUE=$EXPECTED_RETURN_VALUE"
fi
expect 0 "accepts 0" "expect.error accepts 0"

# ============================================================================
# T18: Test toggleDebug sets STEP_DEBUG to OFF
# ============================================================================
export STEP_DEBUG=ON
export LOG_LEVEL=3

test.case - "T18: toggleDebug sets STEP_DEBUG OFF" \
  toggleDebug

if [ "$STEP_DEBUG" = "OFF" ]; then
  create.result 0 "STEP_DEBUG=OFF"
else
  create.result 1 "STEP_DEBUG=$STEP_DEBUG"
fi
expect 0 "STEP_DEBUG=OFF" "toggleDebug sets STEP_DEBUG OFF"

# Reset
export STEP_DEBUG=OFF

# ============================================================================
# T19: Test onReturn handles return value
# ============================================================================
test_return_func() {
  return 42
}

# Set up trap temporarily
trap 'onReturn ${?}' RETURN 2>/dev/null

test.case - "T19: onReturn handles function return" \
  test_return_func

# onReturn should have been called
if [ "$RETURN_VALUE" -eq 42 ]; then
  create.result 0 "return captured"
else
  create.result 1 "RETURN_VALUE=$RETURN_VALUE"
fi
expect 0 "return captured" "onReturn handles function return"

# Clear trap
trap - RETURN 2>/dev/null

# ============================================================================
# T20: Test PS4 format is set for trace
# ============================================================================
test.case - "T20: PS4 trace format available" \
  echo "checking PS4"

# PS4 should be set when debug is sourced at high level
if [ -n "$PS4" ]; then
  create.result 0 "PS4 is set"
else
  create.result 1 "PS4 is empty"
fi
expect 0 "PS4 is set" "PS4 trace format available"

# ============================================================================
# T21: Test onError suppresses expected errors (exit code = EXPECTED_RETURN_VALUE)
# ============================================================================
# Set up ERR trap for this test
trap 'onError $?' ERR 2>/dev/null

export EXPECTED_RETURN_VALUE=1
export RETURN_VALUE=0

test.case - "T21: onError suppresses expected error code" \
  echo "testing onError suppression"

# Simulate onError being called with exit code 1 (the expected value)
onError 1

if [ "$RETURN_VALUE" -eq 1 ] && [ "$EXPECTED_RETURN_VALUE" -eq 1 ]; then
  create.result 0 "expected error suppressed"
else
  create.result 1 "RETURN_VALUE=$RETURN_VALUE EXPECTED_RETURN_VALUE=$EXPECTED_RETURN_VALUE"
fi
expect 0 "expected error suppressed" "onError suppresses expected error and resets EXPECTED_RETURN_VALUE"

# Clear trap
trap - ERR 2>/dev/null

# ============================================================================
# Cleanup
# ============================================================================
cleanup_debug_test

# ============================================================================
# Save results
# ============================================================================
test.suite.save.results
