#!/bin/bash
# Example regression test runner
# Usage: ./tests/run_examples.sh [--update]
#   --update: regenerate expected output and exit code files

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SX="$ROOT_DIR/zig-out/bin/sx"
EXPECTED_DIR="$SCRIPT_DIR/expected"

PASS=0
FAIL=0
SKIP=0
TIMEOUT_COUNT=0
UPDATE=0
TIMEOUT=10

if [[ "${1:-}" == "--update" ]]; then
    UPDATE=1
fi

normalize() {
    sed 's/0x[0-9a-f]\{4,\}/0xADDR/g'
}

# Normalize `sx ir` output for snapshot diffing. Strips host-specific
# noise so a snapshot taken on one macOS machine matches another:
#   - target triple / datalayout / module-id headers
#   - function-attribute groups (target-cpu, frame-pointer string)
#   - LLVM's auto-suffixed temporary names (%add1, %icmp29 → %add, %icmp)
normalize_ir() {
    sed -E \
        -e '/^; ModuleID =/d' \
        -e '/^source_filename =/d' \
        -e '/^target datalayout =/d' \
        -e '/^target triple =/d' \
        -e '/^attributes #[0-9]+ = \{/d' \
        -e 's/%([a-z]+)[0-9]+/%\1N/g'
}

for expected_file in "$EXPECTED_DIR"/*.txt; do
    name=$(basename "$expected_file" .txt)
    sx_file="$ROOT_DIR/examples/${name}.sx"
    exit_file="$EXPECTED_DIR/${name}.exit"

    if [[ ! -f "$sx_file" ]]; then
        SKIP=$((SKIP + 1))
        continue
    fi

    printf "  %-30s" "$name"
    actual=$(timeout "$TIMEOUT" "$SX" run "$sx_file" 2>&1 | normalize)
    actual_exit=${PIPESTATUS[0]}

    if [[ $actual_exit -eq 124 ]]; then
        TIMEOUT_COUNT=$((TIMEOUT_COUNT + 1))
        echo "TIMEOUT (>${TIMEOUT}s)"
        continue
    fi

    # Optional IR-shape snapshot. When tests/expected/<name>.ir exists,
    # also diff `sx ir <file>` against it. Used to lock down lowering
    # changes that don't show up in runtime output (e.g., selector
    # interning trims sel_registerName calls without changing what
    # the program prints).
    ir_file="$EXPECTED_DIR/${name}.ir"
    actual_ir=""
    has_ir_snapshot=false
    if [[ -f "$ir_file" || ( $UPDATE -eq 1 && -f "$EXPECTED_DIR/${name}.ir" ) ]]; then
        has_ir_snapshot=true
    fi
    if $has_ir_snapshot; then
        actual_ir=$("$SX" ir "$sx_file" 2>&1 | normalize_ir)
    fi

    if [[ $UPDATE -eq 1 ]]; then
        echo "$actual" > "$expected_file"
        echo "$actual_exit" > "$exit_file"
        if $has_ir_snapshot; then
            echo "$actual_ir" > "$ir_file"
        fi
        echo "  updated $name (exit=$actual_exit)"
        continue
    fi

    expected=$(cat "$expected_file" | normalize)
    expected_exit=0
    if [[ -f "$exit_file" ]]; then
        expected_exit=$(cat "$exit_file")
    fi
    expected_ir=""
    if $has_ir_snapshot; then
        expected_ir=$(cat "$ir_file" | normalize_ir)
    fi

    output_ok=true
    exit_ok=true
    ir_ok=true

    if [[ "$actual" != "$expected" ]]; then
        output_ok=false
    fi
    if [[ "$actual_exit" != "$expected_exit" ]]; then
        exit_ok=false
    fi
    if $has_ir_snapshot && [[ "$actual_ir" != "$expected_ir" ]]; then
        ir_ok=false
    fi

    if $output_ok && $exit_ok && $ir_ok; then
        PASS=$((PASS + 1))
        echo "ok"
    else
        FAIL=$((FAIL + 1))
        echo "FAIL"
        if ! $output_ok; then
            diff <(echo "$expected") <(echo "$actual") || true
        fi
        if ! $exit_ok; then
            echo "  exit code: expected=$expected_exit actual=$actual_exit"
        fi
        if ! $ir_ok; then
            echo "  IR diff:"
            diff <(echo "$expected_ir") <(echo "$actual_ir") || true
        fi
    fi
done

if [[ $UPDATE -eq 1 ]]; then
    echo "Updated all expected output files."
    exit 0
fi

echo "$PASS passed, $FAIL failed, $SKIP skipped, $TIMEOUT_COUNT timed out"
[[ $FAIL -eq 0 && $TIMEOUT_COUNT -eq 0 ]]
