; persist/snapshot — checkpoint a projection so a read model rebuilds as
; snapshot + tail instead of a full replay. A snapshot is just a projection
; state {:value :seq} stored in the kv facet under a namespaced key. The
; headline property (tested both ways): snapshot + tail == full replay. Replay
; is pure — it depends only on the stored snapshot and the log tail, never a
; clock. Requires: lib/persist/project.sx, lib/persist/kv.sx.

(define persist/snapshot-key (fn (name) (str "snapshot/" name)))

; load the stored snapshot for name, or a fresh {:value seed :seq 0} if none
(define
  persist/snapshot-load
  (fn
    (b name seed)
    (persist/kv-get-or b (persist/snapshot-key name) {:value seed :seq 0})))

; store a projection state as the snapshot for name; returns the state
(define
  persist/snapshot-save
  (fn (b name state) (persist/kv-put b (persist/snapshot-key name) state)))

(define
  persist/snapshot-exists?
  (fn (b name) (persist/kv-has? b (persist/snapshot-key name))))

; replay = snapshot + tail: load the snapshot then fold events after it
(define
  persist/replay
  (fn
    (b stream name step seed)
    (persist/project-resume b stream step (persist/snapshot-load b name seed))))

; replay then persist the new snapshot; returns the updated state
(define
  persist/checkpoint
  (fn
    (b stream name step seed)
    (let
      ((state (persist/replay b stream name step seed)))
      (begin (persist/snapshot-save b name state) state))))
