;; lib/flow/tests/basic.sx — Phase 1: declarative DAG + sequential execution.

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

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

;; Run a Scheme flow-program string and return its final value.
(define flow-b (fn (src) (flow-run src)))

;; Scheme strings are boxed as {:scm-string "..."}; unwrap to a host string.
(define flow-bs (fn (src) (get (flow-run src) :scm-string)))

;; ── single node ─────────────────────────────────────────────────
(flow-basic-test
  "node: identity passes input through"
  (flow-b "(flow/start flow-id 7)")
  7)
(flow-basic-test
  "node: const ignores input"
  (flow-b "(flow/start (flow-const 99) 1)")
  99)
(flow-basic-test
  "node: bare lambda is a node"
  (flow-b "(flow/start (lambda (x) (* x x)) 6)")
  36)

;; ── linear sequence ─────────────────────────────────────────────
(flow-basic-test
  "sequence: empty is identity"
  (flow-b "(flow/start (sequence) 42)")
  42)
(flow-basic-test
  "sequence: single child"
  (flow-b "(flow/start (sequence (lambda (x) (+ x 1))) 41)")
  42)
(flow-basic-test
  "sequence: two children thread"
  (flow-b
    "(flow/start (sequence (lambda (x) (+ x 1)) (lambda (x) (* x 10))) 4)")
  50)
(flow-basic-test
  "sequence: three children thread"
  (flow-b
    "(flow/start (sequence (lambda (x) (+ x 1)) (lambda (x) (* x 2)) (lambda (x) (- x 3))) 5)")
  9)

;; ── data flow between nodes ─────────────────────────────────────
(flow-basic-test
  "data flow: string accumulation"
  (flow-bs
    "(flow/start (sequence (lambda (s) (string-append s \"-a\")) (lambda (s) (string-append s \"-b\"))) \"x\")")
  "x-a-b")
(flow-basic-test
  "data flow: list build"
  (flow-b
    "(flow/start (sequence (lambda (x) (cons x (list))) (lambda (xs) (cons 0 xs))) 7)")
  (list 0 7))

;; ── defflow ─────────────────────────────────────────────────────
(flow-basic-test
  "defflow: names a flow"
  (flow-b
    "(defflow inc2 (sequence (lambda (x) (+ x 1)) (lambda (x) (+ x 1)))) (flow/start inc2 40)")
  42)
(flow-basic-test
  "defflow: reusable"
  (flow-b
    "(defflow dbl (lambda (x) (* x 2))) (+ (flow/start dbl 3) (flow/start dbl 10))")
  26)

;; ── parallel (sequential semantics, join into list) ─────────────
(flow-basic-test
  "parallel: fans input to all branches"
  (flow-b
    "(flow/start (parallel (lambda (x) (+ x 1)) (lambda (x) (* x 2)) (lambda (x) (- x 3))) 10)")
  (list 11 20 7))
(flow-basic-test
  "parallel: empty joins to empty list"
  (flow-b "(flow/start (parallel) 5)")
  (list))
(flow-basic-test
  "parallel: single branch"
  (flow-b "(flow/start (parallel (lambda (x) (* x x))) 9)")
  (list 81))

;; ── nested composition ──────────────────────────────────────────
(flow-basic-test
  "nested: sequence of sequences"
  (flow-b
    "(flow/start (sequence (sequence (lambda (x) (+ x 1)) (lambda (x) (+ x 1))) (sequence (lambda (x) (* x 3)))) 0)")
  6)
(flow-basic-test
  "nested: parallel inside sequence, join then reduce"
  (flow-b
    "(flow/start (sequence (parallel (lambda (x) (+ x 1)) (lambda (x) (* x 2))) (lambda (xs) (apply + xs))) 10)")
  31)
(flow-basic-test
  "nested: sequence inside parallel branch"
  (flow-b
    "(flow/start (parallel (sequence (lambda (x) (+ x 1)) (lambda (x) (* x 2))) (lambda (x) x)) 5)")
  (list 12 5))

;; ── publish-shaped flow (the architecture sketch) ───────────────
(flow-basic-test
  "publish: write -> (review | spell) -> join lengths"
  (flow-b
    "(defflow publish (sequence (lambda (draft) (string-append draft \"!\")) (parallel (lambda (c) (string-length c)) (lambda (c) (string-length (string-append c \"?\")))))) (flow/start publish \"hi\")")
  (list 3 4))

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