#!/usr/bin/env bash

### shfmt -w -s -ci -sr -kp -fn tests/unit/run-tests.sh

#------------------------------------------------------------------------------
# Test Runner Script
# Runs tests in chunks, distributing them across processors
# Supports moving test artifacts to specified output directory
#------------------------------------------------------------------------------

# Exit on error, undefined vars, and propagate pipe failures
set -euo pipefail

# Global variable for test failures
declare -i TESTS_FAILED=0

# Function to show usage
show_usage()
{
	echo "Usage: $0 [chunks] [chunk_number] [options]"
	echo "  chunks        : Number of chunks to split tests into (default: 1)"
	echo "  chunk_number  : Which chunk to run (default: 1)"
	echo "Options:"
	echo "  --dry-run     : Show test distribution without running tests"
	echo "  --output-dir  : Directory to store test artifacts (will be created if needed)"
	echo "  -h|--help     : Show this help message"
	exit 1
}

# Function to validate numeric input
validate_number()
{
	local val=$1
	local name=$2

	if ! [[ $val =~ ^[0-9]+$ ]]; then
		echo "Error: $name must be a positive number, got: $val"
		exit 1
	fi

	if [ "$val" -lt 1 ]; then
		echo "Error: $name must be greater than 0, got: $val"
		exit 1
	fi
}

# Function to format duration in human-readable form
format_duration()
{
	local duration=$1
	local minutes=$((duration / 60))
	local seconds=$((duration % 60))
	printf "%02d:%02d" $minutes $seconds
}

# Function to move test artifacts to output directory
move_artifacts()
{
	local output_dir=$1

	# Create output directory if it doesn't exist
	mkdir -p "$output_dir"

	# Move HTML logs and backtrace files if they exist
	# Using || true to prevent script failure if no files match
	(mv log_run-tests_*.html "$output_dir" 2> /dev/null || true)
	(mv backtrace_*.txt "$output_dir" 2> /dev/null || true)

	# Check if any files were moved
	if [ -n "$(ls -A "$output_dir" 2> /dev/null)" ]; then
		echo "Test artifacts moved to: $output_dir"
	else
		echo "No test artifacts found to move"
	fi
}

# Parse command line arguments
chunks=1
chunk_number=1
dry_run=false
output_dir=""

while [[ $# -gt 0 ]]; do
	case $1 in
		--dry-run)
			dry_run=true
			shift
			;;
		--output-dir)
			if [ -n "${2:-}" ]; then
				output_dir="$2"
				shift 2
			else
				echo "Error: --output-dir requires a directory path"
				exit 1
			fi
			;;
		-h | --help)
			show_usage
			;;
		*)
			if [[ $chunks -eq 1 ]]; then
				chunks=$1
			elif [[ $chunk_number -eq 1 ]]; then
				chunk_number=$1
			else
				echo "Error: Unknown argument $1"
				show_usage
			fi
			shift
			;;
	esac
done

# Validate numeric inputs
validate_number "$chunks" "chunks"
validate_number "$chunk_number" "chunk_number"

# Validate chunk parameters
if [ "$chunk_number" -gt "$chunks" ]; then
	echo "Error: chunk_number ($chunk_number) cannot be greater than total chunks ($chunks)"
	exit 1
fi

# Get list of tests from make
echo "Fetching test list..."
TESTS=$(make -s -C ../.. print_tests 2>/dev/null) || {
	echo "Error: Failed to fetch test list"
	exit 1
}

# Split tests into array
IFS=$'\n' read -d '' -r -a all_tests <<< "$TESTS" || true # || true to handle last line without newline

# Check if any tests were found
if [ ${#all_tests[@]} -eq 0 ]; then
	echo "Error: No tests found!"
	exit 1
fi

# Get total number of tests from array length
total_tests=${#all_tests[@]}

# Select tests for this chunk
chunk_tests=()
for ((i = chunk_number - 1; i < total_tests; i += chunks)); do
	chunk_tests+=("${all_tests[$i]}")
done

# Size of this chunk
chunk_size=${#chunk_tests[@]}

# Print execution information
echo ""
echo "Test Distribution Information:"
echo "Total tests found: $total_tests"
echo "Chunk size: $chunk_size"
echo "Running chunk $chunk_number/$chunks"
if [ -n "$output_dir" ]; then
	echo "Artifacts will be stored in: $output_dir"
fi
echo ""
echo "Tests to be executed:"
for i in "${!chunk_tests[@]}"; do
	printf "%3d) %s\n" "$((i + 1))" "${chunk_tests[$i]}"
done
echo ""

# Exit here if dry run
if [ "$dry_run" = true ]; then
	echo "Dry run complete. Use without --dry-run to execute tests."
	exit 0
fi

# Record start time
start_time=$(date +%s)

# Run tests sequentially within the chunk
if ! make -f run-tests.mk TEST_LIST="${chunk_tests[*]}"; then
	TESTS_FAILED=1
fi

# Move artifacts if output directory was specified
if [ -n "$output_dir" ]; then
	move_artifacts "$output_dir"
fi

# Record end time and calculate duration
end_time=$(date +%s)
duration=$((end_time - start_time))

# Print timing results and statistics
echo ""
echo "Execution Summary for chunk $chunk_number/$chunks:"
echo "Started at : $(date -d @$start_time '+%Y-%m-%d %H:%M:%S')"
echo "Finished at: $(date -d @$end_time '+%Y-%m-%d %H:%M:%S')"
echo "Duration   : $(format_duration $duration)"
echo "Status     : $([ $TESTS_FAILED -eq 0 ] && echo "SUCCESS" || echo "FAILED")"
echo ""

# Exit with appropriate status code
exit $TESTS_FAILED