;; lib/mod/tests/temporal.sx — Ext 12: burst detection over a time window.

(define mod-tm-count 0)
(define mod-tm-pass 0)
(define mod-tm-fail 0)
(define mod-tm-failures (list))

(define
  mod-tm-test!
  (fn
    (name got expected)
    (begin
      (set! mod-tm-count (+ mod-tm-count 1))
      (if
        (= got expected)
        (set! mod-tm-pass (+ mod-tm-pass 1))
        (begin
          (set! mod-tm-fail (+ mod-tm-fail 1))
          (append!
            mod-tm-failures
            (str name "\n    expected: " expected "\n    got:      " got)))))))

(define
  mod-tm-at
  (fn (id about t) (mod/with-at (mod/mk-report id "u" about "off-topic") t)))

(define
  mod-tm-rules
  (list
    (mod/mk-rule "burst-hide" :hide (list (list :burst-at-least 3)))
    (mod/mk-rule "default-keep" :keep (list))))

;; ── window-count helper ──

(define
  mod-tm-burst
  (list
    (mod-tm-at "r1" "bob" 10)
    (mod-tm-at "r2" "bob" 11)
    (mod-tm-at "r3" "bob" 12)))
(define
  mod-tm-slow
  (list
    (mod-tm-at "r1" "bob" 1)
    (mod-tm-at "r2" "bob" 2)
    (mod-tm-at "r3" "bob" 12)))

(mod-tm-test!
  "window-count: all 3 within window"
  (mod/window-count "bob" mod-tm-burst 12 5)
  3)
(mod-tm-test!
  "window-count: only 1 within window"
  (mod/window-count "bob" mod-tm-slow 12 5)
  1)
(mod-tm-test!
  "window-count: subject filter"
  (mod/window-count "eve" mod-tm-burst 12 5)
  0)

;; ── burst fires; slow accumulation does not ──

(mod-tm-test!
  "burst (3 in window) → hide"
  (get
    (mod/decide-temporal
      (first mod-tm-burst)
      mod-tm-burst
      mod-tm-rules
      12
      5)
    :action)
  "hide")
(mod-tm-test!
  "slow accumulation (1 in window) → keep"
  (get
    (mod/decide-temporal
      (first mod-tm-slow)
      mod-tm-slow
      mod-tm-rules
      12
      5)
    :action)
  "keep")

;; ── contrast: the plain count rule fires on BOTH (3 total reports) ──
(mod-tm-test!
  "count rule fires on slow case (distinct from burst)"
  (get
    (mod/decide-report (first mod-tm-slow) mod-tm-slow mod/default-rules)
    :action)
  "escalate")

;; ── decision shape ──

(define
  mod-tm-d
  (mod/decide-temporal
    (first mod-tm-burst)
    mod-tm-burst
    mod-tm-rules
    12
    5))
(mod-tm-test! "burst decision rule" (get mod-tm-d :rule) "burst-hide")
(mod-tm-test!
  "burst decision tagged strategy"
  (get mod-tm-d :strategy)
  "temporal")
(mod-tm-test!
  "burst recorded in proof"
  (get (get mod-tm-d :proof) :burst)
  3)
(mod-tm-test!
  "burst proof goal solved"
  (get (first (get (get mod-tm-d :proof) :goals)) :solved)
  true)

;; ── window boundary is inclusive ──

(define
  mod-tm-edge
  (list
    (mod-tm-at "r1" "bob" 7)
    (mod-tm-at "r2" "bob" 8)
    (mod-tm-at "r3" "bob" 9)))
(mod-tm-test!
  "window boundary inclusive (now-window = at)"
  (mod/window-count "bob" mod-tm-edge 12 5)
  3)

;; ── schema :at round-trips and survives evidence attach ──

(mod-tm-test!
  "report-at reads timestamp"
  (mod/report-at (mod-tm-at "r1" "bob" 42))
  42)
(mod-tm-test!
  "default report-at is 0"
  (mod/report-at (mod/mk-report "r1" "a" "b" "x"))
  0)
(mod-tm-test!
  "attach-evidence preserves :at"
  (mod/report-at
    (mod/attach-evidence
      (mod-tm-at "r1" "bob" 42)
      (mod/mk-evidence "k" "v")))
  42)

;; ── cond->goal :burst-at-least ──

(mod-tm-test!
  "cond->goal :burst-at-least"
  (mod/cond->goal (list :burst-at-least 3) "Id")
  "report(Id, _, Sb), burst_count(Sb, Nb), Nb >= 3")

(define mod-temporal-tests-run! (fn () {:failures mod-tm-failures :total mod-tm-count :passed mod-tm-pass :failed mod-tm-fail}))
