;; lib/flow/tests/host.sx — Phase 8: host integration ABI (request/await/host-queue/driver).

(define flow-hst-pass 0)
(define flow-hst-fail 0)
(define flow-hst-fails (list))

(define
  flow-hst-test
  (fn
    (name actual expected)
    (if
      (= actual expected)
      (set! flow-hst-pass (+ flow-hst-pass 1))
      (begin
        (set! flow-hst-fail (+ flow-hst-fail 1))
        (append! flow-hst-fails {:name name :expected expected :actual actual})))))

(define flow-hst (fn (src) (flow-run src)))

;; ── request envelope ────────────────────────────────────────────
(flow-hst-test
  "request: suspends with a typed envelope"
  (flow-hst
    "(car (cdr (cdr (flow/start (lambda (x) (request (quote render) x)) 5))))")
  (list "flow-request" "render" 5))
(flow-hst-test
  "request?: recognizes an envelope"
  (flow-hst "(request? (list (quote flow-request) (quote human) 1))")
  true)
(flow-hst-test
  "request?: a plain tag is not a request"
  (flow-hst "(request? (list (quote review) 1))")
  false)
(flow-hst-test
  "request-kind / request-payload: parse the envelope"
  (flow-hst
    "(define t (list (quote flow-request) (quote render) (list (quote recipe) 7))) (list (request-kind t) (request-payload t))")
  (list "render" (list "recipe" 7)))

;; ── named decision points ───────────────────────────────────────
(flow-hst-test
  "await-human: is a request of kind human"
  (flow-hst
    "(car (cdr (cdr (flow/start (lambda (x) (await-human x)) (quote approve?)))))")
  (list "flow-request" "human" "approve?"))
(flow-hst-test
  "await-render: is a request of kind render"
  (flow-hst
    "(car (cdr (cdr (flow/start (lambda (x) (await-render x)) (quote recipe)))))")
  (list "flow-request" "render" "recipe"))
(flow-hst-test
  "request: the host's resume value flows back into the flow"
  (flow-hst
    "(defflow f (sequence (lambda (x) (await-render x)) (lambda (art) (list (quote got) art)))) (define id (car (cdr (flow/start f 1)))) (flow/resume id (quote the-artifact))")
  (list "got" "the-artifact"))

;; ── host work queue ─────────────────────────────────────────────
(flow-hst-test
  "flow-host-requests: lists (id kind payload) for pending requests"
  (flow-hst
    "(flow/start (lambda (x) (await-render x)) 99) (flow-host-requests)")
  (list (list 1 "render" 99)))
(flow-hst-test
  "flow-host-requests: excludes bare (non-request) suspends"
  (flow-hst
    "(defflow a (lambda (x) (await-render x))) (defflow b (lambda (x) (suspend (quote plain)))) (flow/start a 1) (flow/start b 2) (flow-host-requests)")
  (list (list 1 "render" 1)))

;; ── the art-dag-shaped host driver loop (manual resumes) ────────
(flow-hst-test
  "host driver: render then human-review then publish"
  (flow-hst
    "(defflow pipeline (sequence (lambda (recipe) (await-render recipe)) (lambda (art) (await-human (list (quote review) art))) (branch (lambda (d) (eq? d (quote approve))) (flow-const (quote published)) (flow-const (fail (quote rejected)))))) (define id (car (cdr (flow/start pipeline 99)))) (define r1 (flow-host-requests)) (flow/resume id (list (quote art) 99)) (define r2 (flow-host-requests)) (flow/resume id (quote approve)) (list r1 r2 (flow/status id) (flow/result id))")
  (list
    (list (list 1 "render" 99))
    (list (list 1 "human" (list "review" (list "art" 99))))
    "done"
    "published"))
(flow-hst-test
  "host driver: rejection at the human gate yields a failure"
  (flow-hst
    "(defflow pipeline (sequence (lambda (recipe) (await-render recipe)) (lambda (art) (await-human (list (quote review) art))) (branch (lambda (d) (eq? d (quote approve))) (flow-const (quote published)) (flow-const (fail (quote rejected)))))) (define id (car (cdr (flow/start pipeline 1)))) (flow/resume id (quote artifact)) (failed? (flow/resume id (quote reject)))")
  true)

;; ── reference driver: host supplies only a dispatch fn ──────────
(flow-hst-test
  "flow-drive-host: one tick services every pending request"
  (flow-hst
    "(flow/start (lambda (x) (await-render x)) 5) (define n (flow-drive-host (lambda (k p) (list (quote done) p)))) (list n (flow/status 1) (flow/result 1))")
  (list 1 "done" (list "done" 5)))
(flow-hst-test
  "flow-run-host: drives a render -> human pipeline to completion"
  (flow-hst
    "(defflow pipeline (sequence (lambda (recipe) (await-render recipe)) (lambda (art) (await-human (list (quote review) art))) (branch (lambda (d) (eq? d (quote approve))) (flow-const (quote published)) (flow-const (fail (quote rejected)))))) (define id (car (cdr (flow/start pipeline 99)))) (define serviced (flow-run-host (lambda (kind payload) (if (eq? kind (quote render)) (list (quote art) payload) (quote approve))) 10)) (list serviced (flow/status id) (flow/result id))")
  (list 2 "done" "published"))
(flow-hst-test
  "flow-run-host: returns 0 when nothing is pending"
  (flow-hst "(flow-run-host (lambda (k p) p) 5)")
  0)
(flow-hst-test
  "flow-run-host: respects the maxticks bound"
  (flow-hst
    "(defflow pipe2 (sequence (lambda (r) (await-render r)) (lambda (a) (await-human a)) (lambda (d) d))) (define id (car (cdr (flow/start pipe2 1)))) (define serviced (flow-run-host (lambda (k p) p) 1)) (list serviced (flow/status id))")
  (list 1 "suspended"))

(define flow-hst-tests-run! (fn () {:total (+ flow-hst-pass flow-hst-fail) :passed flow-hst-pass :failed flow-hst-fail :fails flow-hst-fails}))
