;; content-on-sx — snapshot cache over the op-log replay.
;;
;; Snapshots are a CACHE, never primary state: the op log stays the source of
;; truth. A snapshot stores a materialised document at a sequence in the persist
;; KV; cached reads start from it and replay only the tail of ops, so they return
;; a document IDENTICAL to a full replay — just faster. Drop the snapshot and
;; nothing is lost.
;;
;; Requires (loaded by harness): store.sx (+ doc.sx, persist event/log/kv/api).

(define content/-snap-key (fn (doc-id) (str "content-snap:" doc-id)))

;; take a snapshot of the current head at the current version. Returns the seq.
(define
  content/snapshot!
  (fn
    (b doc-id)
    (let
      ((seq (content/version-count b doc-id)))
      (begin (persist/kv-put b (content/-snap-key doc-id) {:doc (content/head b doc-id) :seq seq}) seq))))

(define
  content/-snapshot
  (fn
    (b doc-id)
    (if
      (persist/kv-has? b (content/-snap-key doc-id))
      (persist/kv-get b (content/-snap-key doc-id))
      nil)))

(define
  content/snapshot-seq
  (fn
    (b doc-id)
    (let
      ((s (content/-snapshot b doc-id)))
      (if (= s nil) 0 (get s :seq)))))

(define
  content/has-snapshot?
  (fn (b doc-id) (persist/kv-has? b (content/-snap-key doc-id))))

(define
  content/drop-snapshot!
  (fn (b doc-id) (persist/kv-delete b (content/-snap-key doc-id))))

;; ── cached reads (transparent: identical result to store.sx replay) ──
(define
  content/-tail-ops
  (fn
    (b doc-id from to)
    (map
      (fn (ev) (persist/event-data ev))
      (filter
        (fn
          (ev)
          (and
            (> (persist/event-seq ev) from)
            (<= (persist/event-seq ev) to)))
        (content/log b doc-id)))))

(define
  content/head-cached
  (fn
    (b doc-id)
    (let
      ((snap (content/-snapshot b doc-id)))
      (if
        (= snap nil)
        (content/head b doc-id)
        (doc-apply-all
          (get snap :doc)
          (content/-tail-ops
            b
            doc-id
            (get snap :seq)
            (content/version-count b doc-id)))))))

(define
  content/at-cached
  (fn
    (b doc-id seq)
    (let
      ((snap (content/-snapshot b doc-id)))
      (if
        (or (= snap nil) (< seq (get snap :seq)))
        (content/at b doc-id seq)
        (doc-apply-all
          (get snap :doc)
          (content/-tail-ops b doc-id (get snap :seq) seq))))))
