#!/usr/bin/env bash
# run.sh — build and execute the time/ acceptance corpus.
#
# Each examples/<case>.el is a self-contained El program with a fn main()
# that prints a single deterministic result line. The runner compiles each
# via the canonical native elc, links it against the shared C runtime, runs
# it, and asserts the output matches the expected value.
#
# The typeerror case is special: the C source is generated successfully (a
# `#error` directive sits at the bottom) but cc must FAIL. Pass = cc fails
# with the expected diagnostic; Fail = cc succeeds (the type rule didn't
# fire) or fails with an unexpected diagnostic.

set -uo pipefail
cd "$(dirname "$0")"

EL_HOME="${EL_HOME:-$(cd ../.. && pwd)}"
ELC="${EL_HOME}/dist/platform/elc"
RUNTIME_DIR="${EL_HOME}/runtime"

if [ ! -x "${ELC}" ]; then
    echo "elc not found at ${ELC}" >&2
    exit 1
fi

PASS=0
FAIL=0
FAILED_NAMES=()

# run_runtime_case <name> <source> <expected> [<extra>]
# Compiles the source via elc, links against the runtime, runs the binary,
# and compares stdout (whitespace-trimmed) against <expected>. If <extra> is
# the literal string "either", any one of the comma-separated values in
# <expected> counts as a pass — used by time-arithmetic where the second
# now() reading is microseconds after the first, which makes the >=
# comparison platform-dependent.
run_runtime_case() {
    local name="$1"
    local src="$2"
    local expected="$3"
    local mode="${4:-exact}"

    local out_c
    local out_bin
    out_c="$(mktemp -t time_test.XXXXXX).c"
    out_bin="$(mktemp -t time_test.XXXXXX)"

    if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then
        echo "FAIL  ${name} — elc emit failed:"
        cat /tmp/time_test.elc.err | sed 's/^/      /'
        FAIL=$((FAIL+1))
        FAILED_NAMES+=("${name}")
        rm -f "${out_c}" "${out_bin}"
        return
    fi

    if ! cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \
            -lcurl -lssl -lcrypto -lpthread -lm -o "${out_bin}" 2>/tmp/time_test.cc.err; then
        echo "FAIL  ${name} — cc failed:"
        cat /tmp/time_test.cc.err | sed 's/^/      /'
        FAIL=$((FAIL+1))
        FAILED_NAMES+=("${name}")
        rm -f "${out_c}" "${out_bin}"
        return
    fi

    local got
    got="$("${out_bin}" 2>&1 | tr -d '[:space:]')"

    if [ "${mode}" = "either" ]; then
        local ok=0
        local IFS=','
        for choice in ${expected}; do
            if [ "${got}" = "${choice}" ]; then ok=1; break; fi
        done
        if [ "${ok}" = "1" ]; then
            echo "PASS  ${name}  (got: ${got})"
            PASS=$((PASS+1))
        else
            echo "FAIL  ${name}  expected one of {${expected}}, got: ${got}"
            FAIL=$((FAIL+1))
            FAILED_NAMES+=("${name}")
        fi
    else
        if [ "${got}" = "${expected}" ]; then
            echo "PASS  ${name}"
            PASS=$((PASS+1))
        else
            echo "FAIL  ${name}  expected: ${expected}, got: ${got}"
            FAIL=$((FAIL+1))
            FAILED_NAMES+=("${name}")
        fi
    fi

    rm -f "${out_c}" "${out_bin}"
}

# run_typeerror_case <name> <source>
# Compiles to C (which must succeed; the violation is recorded but codegen
# still completes), then asserts cc FAILS with the temporal-type-error
# directive in its output.
run_typeerror_case() {
    local name="$1"
    local src="$2"

    local out_c
    out_c="$(mktemp -t time_test.XXXXXX).c"

    if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then
        echo "FAIL  ${name} — elc emit failed (expected emit-then-cc-fail):"
        cat /tmp/time_test.elc.err | sed 's/^/      /'
        FAIL=$((FAIL+1))
        FAILED_NAMES+=("${name}")
        rm -f "${out_c}"
        return
    fi

    if cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \
           -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/time_test_should_not_link 2>/tmp/time_test.cc.err; then
        echo "FAIL  ${name} — cc unexpectedly succeeded; type rule did not fire"
        FAIL=$((FAIL+1))
        FAILED_NAMES+=("${name}")
        rm -f "${out_c}" /tmp/time_test_should_not_link
        return
    fi

    if grep -q "temporal type error" /tmp/time_test.cc.err; then
        echo "PASS  ${name}  (cc rejected with: $(grep "temporal type error" /tmp/time_test.cc.err | head -1 | tr -d '\n'))"
        PASS=$((PASS+1))
    else
        echo "FAIL  ${name} — cc failed but without the expected temporal-type-error message:"
        cat /tmp/time_test.cc.err | sed 's/^/      /'
        FAIL=$((FAIL+1))
        FAILED_NAMES+=("${name}")
    fi
    rm -f "${out_c}"
}

echo "==> Running time-types acceptance corpus"
echo

run_runtime_case   "time-literals"   examples/time-literals.el   "30"
run_runtime_case   "time-arithmetic" examples/time-arithmetic.el "0,1" either
run_runtime_case   "time-comparison" examples/time-comparison.el "1"
run_typeerror_case "time-typeerror"  examples/time-typeerror.el
run_runtime_case   "ttl-cache"       examples/ttl-cache.el       "hello"
run_runtime_case   "sleep-typed"     examples/sleep-typed.el     "1"

echo
echo "${PASS} passed, ${FAIL} failed"
if [ "${FAIL}" -gt 0 ]; then
    echo "failed: ${FAILED_NAMES[*]}"
    exit 1
fi
exit 0
