;; lib/commerce/refund.sx — refund lifecycle as a second flow-on-sx flow.
;;
;; A refund is request → approve → settle, with TWO genuine suspension points:
;; approval (a human/policy decision) and settlement (the provider issuing the
;; refund). Like order.sx the flow is pure orchestration carrying only the
;; order-id; the SX driver does all ledger IO and reuses order.sx's generic flow
;; helpers (order-flow-waiting/-resume/-status, order-susp-id).
;;
;;   refund-begin!   → ledger :refund-requested, flow suspends at 'approve
;;   refund-approve! → resume past approval, flow suspends at 'settle
;;   refund-settle!  → ledger :refunded (idempotent), flow completes
;;   refund-reject!  → ledger :refund-rejected, flow cancelled
;;
;; Only :refunded moves the books (recon.sx), so a requested-but-unsettled or
;; rejected refund leaves reconciliation unchanged.

(define
  refund-flow-src
  "(defflow refund-lifecycle (lambda (oid) (begin (request (quote approve) oid) (request (quote settle) oid))))")

(define
  refund-make-env
  (fn
    ()
    (let
      ((env (flow-make-env)))
      (begin (flow-run-in env refund-flow-src) env))))

;; Register the refund flow into an existing (e.g. order) env.
(define
  refund-flow-load!
  (fn (env) (begin (flow-run-in env refund-flow-src) env)))

(define
  refund-flow-start
  (fn
    (env oid)
    (flow-run-in env (str "(flow/start refund-lifecycle \"" oid "\")"))))

;; --- ledger writes ---

(define
  refund-request
  (fn
    (b oid ref at amount)
    (persist/append-once
      b
      (order-stream oid)
      (str "refund-req/" ref)
      :refund-requested at
      {:amount amount :ref ref})))

;; --- lifecycle ---

;; Open a refund: record the request, start the flow, suspend at approval.
(define
  refund-begin!
  (fn
    (env b oid ref at amount)
    (begin
      (refund-request b oid ref at amount)
      (order-susp-id (refund-flow-start env oid)))))

(define
  refund-approve!
  (fn
    (env id)
    (if
      (= (order-flow-waiting env id) "approve")
      (begin (order-flow-resume env id :approved) :approved)
      :not-pending-approval)))

(define
  refund-reject!
  (fn
    (env b oid id at reason)
    (if
      (= (order-flow-waiting env id) "approve")
      (begin
        (persist/append b (order-stream oid) :refund-rejected at {:reason reason})
        (flow-run-in env (str "(flow/cancel " id ")"))
        :rejected)
      :not-pending-approval)))

;; Settle (provider issued the refund): idempotent — only acts while waiting on
;; settle, so a replayed provider callback returns :already-settled.
(define
  refund-settle!
  (fn
    (env b id oid ref at amount)
    (if
      (= (order-flow-waiting env id) "settle")
      (begin
        (order-refund b oid ref at amount)
        (order-flow-resume env id :settled)
        :settled)
      :already-settled)))
