;; lib/flow/tests/integration.sx — Phase 7: end-to-end flows composing every phase.

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

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

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

;; The order-processing flow, defined once per program via this prelude string:
;;   validate amount (attempt: fail if <= 0)
;;   -> suspend for payment confirmation (resume value = confirmed amount)
;;   -> branch: confirmed>0 ? record on the ledger peer : declined failure
(define
  order-prelude
  "(flow-peer-register! (quote ledger) (list (list (quote record) (lambda (amt) (list (quote recorded) amt)))))\n   (defflow order\n     (attempt\n       (lambda (amt) (if (> amt 0) amt (fail (quote invalid-amount))))\n       (lambda (amt) (suspend (quote await-payment)))\n       (branch (lambda (amt) (> amt 0))\n               (remote-node (quote ledger) (quote record))\n               (flow-const (fail (quote declined))))))")

;; ── happy path through every phase ──────────────────────────────
(flow-int-test
  "order: validate -> suspend -> resume -> branch -> federate"
  (flow-i
    (str
      order-prelude
      "(define id (car (cdr (flow/start order 100)))) (flow/resume id 250)"))
  (list "recorded" 250))
(flow-int-test
  "order: starting suspends awaiting payment"
  (flow-i
    (str
      order-prelude
      "(define s (flow/start order 100)) (list (car s) (car (cdr (cdr s))))"))
  (list "flow-suspended" "await-payment"))
(flow-int-test
  "order: invalid amount fails up front and never suspends"
  (flow-i
    (str
      order-prelude
      "(define r (flow/start order -5)) (list (failed? r) (fail-reason r))"))
  (list true "invalid-amount"))
(flow-int-test
  "order: a declined payment yields a failure value"
  (flow-i
    (str
      order-prelude
      "(define id (car (cdr (flow/start order 100)))) (failed? (flow/resume id 0))"))
  true)

;; ── crash recovery mid-flow ─────────────────────────────────────
(flow-int-test
  "order: survives a simulated crash between suspend and resume"
  (flow-i
    (str
      order-prelude
      "(define id (car (cdr (flow/start order 100)))) (define saved (flow-store-export)) (set! flow-store (list)) (flow-store-import! saved) (flow/resume id 250)"))
  (list "recorded" 250))

;; ── handoff to a peer mid-flow ──────────────────────────────────
(flow-int-test
  "order: hands off to a peer that resumes and completes"
  (flow-i
    (str
      order-prelude
      "(define id (car (cdr (flow/start order 100)))) (flow-replicate-to (quote nodeB)) (set! flow-store (list)) (flow-restore-from (quote nodeB)) (flow/resume id 250)"))
  (list "recorded" 250))

;; ── introspection during the flow's life ────────────────────────
(flow-int-test
  "order: pending shows what the flow awaits, then result after resume"
  (flow-i
    (str
      order-prelude
      "(define id (car (cdr (flow/start order 100)))) (define p (flow/pending)) (flow/resume id 250) (list p (flow/status id) (flow/result id))"))
  (list
    (list (list 1 "await-payment"))
    "done"
    (list "recorded" 250)))

;; ── onboarding: two human steps + cancellation ──────────────────
(define
  onboard-prelude
  "(defflow onboard\n     (sequence\n       (lambda (user) (+ user 1))\n       (lambda (x) (suspend (quote confirm-email)))\n       (lambda (x) (suspend (quote complete-profile)))\n       (lambda (x) (list (quote onboarded) x))))")

(flow-int-test
  "onboard: two suspends resume in order to completion"
  (flow-i
    (str
      onboard-prelude
      "(define id (car (cdr (flow/start onboard 0)))) (flow/resume id 7) (flow/resume id 9)"))
  (list "onboarded" 9))
(flow-int-test
  "onboard: the second pending tag appears after the first resume"
  (flow-i
    (str
      onboard-prelude
      "(define id (car (cdr (flow/start onboard 0)))) (flow/resume id 7) (car (cdr (car (flow/pending))))"))
  "complete-profile")
(flow-int-test
  "onboard: cancelling abandons the flow"
  (flow-i
    (str
      onboard-prelude
      "(define id (car (cdr (flow/start onboard 0)))) (flow/cancel id) (list (flow/status id) (car (flow/resume id 7)))"))
  (list "cancelled" "flow-error"))

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