#!/usr/bin/env bash
# M-INTEROCEPTION P2 gate: chronoception (ENGRAM_CHRONOCEPTION).
# Throwaway HOME + /tmp only. TC defaults to 3600s; we pin it for the math.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RTSRC="$("$HERE/../../scripts/el-runtime-sources.sh" "$HERE/../../lang/runtime")"
# The runtime is MULTI-FILE (lang/runtime/SOURCES). This harness used to link
# el_runtime.c + engram_store.c only, which stopped linking once el_runtime.c
# began calling into the other engram siblings. Unquoted on purpose: a list.
SSLFLAGS=""
if command -v brew >/dev/null 2>&1 && O="$(brew --prefix openssl@3 2>/dev/null)"; then
  SSLFLAGS="-I$O/include -L$O/lib"
fi
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p2-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
export ENGRAM_CHRONO_TC=3600           # pin cooling time-constant for the math
unset ENGRAM_STORE
fail=0

echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p2_chrono.c" $RTSRC $SSLFLAGS \
    -lcurl -lssl -lcrypto -lpthread -lm -lm -o "$WORK/p2" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }

sum_wm(){ python3 -c "import json,sys; g=json.load(open('$1')); print(sum(n.get('working_memory_weight',0) for n in g['nodes']))"; }

echo
echo "== (a) cooling scales with dt (flag ON) =="
for DT in 600000 1800000 3600000 7200000; do    # 600s,1800s,3600s,7200s at TC=3600
  D="$WORK/dt$DT"; mkdir -p "$D"
  ( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" once "$D" "$DT" ) >"$D/out.txt" 2>&1
  MAG=$(grep MAGNITUDE "$D/out.txt" | awk '{print $2}')
  WM=$(sum_wm "$D/field.json")
  PRED=$(python3 -c "import math; print(round(1-math.exp(-$DT/1000/3600),6))")
  echo "    dt=${DT}ms  magnitude=$MAG  predicted 1-exp(-dt/TC)=$PRED  field_wm_sum=$WM"
  python3 -c "import sys; m=float('$MAG'); p=float('$PRED'); sys.exit(0 if abs(m-p)<1e-4 else 1)" \
     && echo "      PASS: magnitude matches exp cooling" || { echo "      FAIL"; fail=1; }
done

echo
echo "== (b) SCALE-INVARIANCE: age(dt) once == age(dt/N) N times (field within float tol) =="
DT=3600000
for N in 2 10 100; do
  DA="$WORK/inv_once_$N"; DB="$WORK/inv_split_$N"; mkdir -p "$DA" "$DB"
  ( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" once  "$DA" "$DT" )      >/dev/null 2>&1
  ( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" split "$DB" "$DT" "$N" ) >/dev/null 2>&1
  WA=$(sum_wm "$DA/field.json"); WB=$(sum_wm "$DB/field.json")
  echo "    N=$N  once_wm=$WA  split_wm=$WB  |delta|=$(python3 -c "print(abs($WA-$WB))")"
  python3 -c "import sys; sys.exit(0 if abs($WA-$WB)<1e-9 else 1)" \
     && echo "      PASS: scale-invariant within 1e-9" || { echo "      FAIL: not scale-invariant"; fail=1; }
done

echo
echo "== (c) REBOOT catch-up: one-shot cooling from persisted last-tick, reports MAGNITUDE not seconds =="
D="$WORK/catch"; mkdir -p "$D"
GAP=3600000   # 1h unconscious
( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$D"; "$WORK/p2" catchup "$D" "$GAP" ) >"$D/out.txt" 2>&1
CMAG=$(grep CATCHUP_MAGNITUDE "$D/out.txt" | awk '{print $2}')
CWM=$(sum_wm "$D/field.json")
PRED=$(python3 -c "import math; print(round(1-math.exp(-$GAP/1000/3600),4))")
echo "    gap=${GAP}ms  catchup_magnitude=$CMAG  predicted=$PRED  field_wm_sum=$CWM (was 0.6)"
python3 -c "import sys; sys.exit(0 if abs(float('$CMAG')-float('$PRED'))<1e-2 else 1)" \
   && echo "      PASS: one-shot catch-up cooled by the elapsed gap, surfaced as a magnitude" \
   || { echo "      FAIL"; fail=1; }
# honesty rail: magnitude is bounded [0,1), NOT an elapsed-seconds number
python3 -c "import sys; m=float('$CMAG'); sys.exit(0 if 0<=m<1 else 1)" \
   && echo "      PASS: magnitude is a bounded drift signal in [0,1), never elapsed seconds" \
   || { echo "      FAIL: magnitude out of [0,1)"; fail=1; }

echo
echo "== (d) OFF path: flag unset -> age & catchup return 0, field untouched =="
D="$WORK/off"; mkdir -p "$D"
( unset ENGRAM_CHRONOCEPTION; export ENGRAM_DATA_DIR="$D"; "$WORK/p2" offcheck "$D" 3600000 ) >"$D/out.txt" 2>&1
cat "$D/out.txt" | sed 's/^/    /'
OFFWM=$(sum_wm "$D/field.json")
# loaded field wm sum = (1.0+0.8+0.6)*0.5 halving = 1.2 ; must be UNCHANGED
echo "    field_wm_sum=$OFFWM (expected 1.2, unchanged)"
python3 -c "import sys; sys.exit(0 if abs($OFFWM-1.2)<1e-9 else 1)" \
   && echo "      PASS: OFF path leaves the field byte-identical (no aging)" \
   || { echo "      FAIL: OFF path modified the field"; fail=1; }

echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
    -I "$INC" "$HERE/test_interoception_p2_chrono.c" $RTSRC $SSLFLAGS \
    -lcurl -lssl -lcrypto -lpthread -lm -lm -o "$WORK/p2.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p2.san" ]; then
  export ASAN_OPTIONS=detect_leaks=0
  DS="$WORK/san"; mkdir -p "$DS"
  ( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$DS"; "$WORK/p2.san" once "$DS" 3600000 ) >/dev/null 2>"$WORK/san.log"
  ( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$DS"; "$WORK/p2.san" catchup "$DS" 3600000 ) >/dev/null 2>>"$WORK/san.log"
  if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san.log"; then
    echo "  FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san.log" | head; fail=1
  else echo "  ok: ASan+UBSan clean"; fi
fi

echo
if [ "$fail" -eq 0 ]; then echo "====== P2 CHRONOCEPTION GATE: PASS ======"; else echo "====== P2 CHRONOCEPTION GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
