;; Phase 1 — ordered block document: apply edit ops, structural moves.
;; Every op returns a NEW document; the input is never mutated.

(st-bootstrap-classes!)
(content-bootstrap-blocks!)
(content-bootstrap-doc!)

(define h (mk-heading "h" 1 "Title"))
(define p1 (mk-text "p1" "First"))
(define p2 (mk-text "p2" "Second"))
(define img (mk-image "img" "/c.png" "cat"))

;; ── empty + construction ──
(define d0 (doc-empty "doc1"))
(content-test "empty id" (doc-id d0) "doc1")
(content-test "empty type" (doc-type d0) "document")
(content-test "empty count" (doc-count d0) 0)
(content-test "doc? on doc" (doc? d0) true)
(content-test "doc? on block" (doc? h) false)

;; ── append + order ──
(define d1 (doc-append (doc-append (doc-append d0 h) p1) p2))
(content-test "append count" (doc-count d1) 3)
(content-test "append order" (doc-ids d1) (list "h" "p1" "p2"))
(content-test "append types" (doc-types d1) (list "heading" "text" "text"))
(content-test "block-at 0" (blk-id (doc-block-at d1 0)) "h")

;; ── append is immutable ──
(content-test "append leaves original" (doc-count d0) 0)

;; ── find / index / has ──
(content-test "find p1" (blk-id (doc-find d1 "p1")) "p1")
(content-test "find missing" (doc-find d1 "nope") nil)
(content-test "index-of p2" (doc-index-of d1 "p2") 2)
(content-test "index-of missing" (doc-index-of d1 "nope") -1)
(content-test "has? yes" (doc-has? d1 "h") true)
(content-test "has? no" (doc-has? d1 "x") false)

;; ── insert-after ──
(define d2 (doc-insert-after d1 img "h"))
(content-test "insert-after order" (doc-ids d2) (list "h" "img" "p1" "p2"))
(content-test
  "insert-after prepend"
  (doc-ids (doc-insert-after d1 img nil))
  (list "img" "h" "p1" "p2"))
(content-test
  "insert-after missing appends"
  (doc-ids (doc-insert-after d1 img "zzz"))
  (list "h" "p1" "p2" "img"))
(content-test "insert-after immutable" (doc-ids d1) (list "h" "p1" "p2"))

;; ── insert-at ──
(content-test
  "insert-at 0"
  (doc-ids (doc-insert-at d1 img 0))
  (list "img" "h" "p1" "p2"))
(content-test
  "insert-at 1"
  (doc-ids (doc-insert-at d1 img 1))
  (list "h" "img" "p1" "p2"))

;; ── update (copy-on-write block) ──
(define d3 (doc-update d1 "p1" "text" "Edited"))
(content-test
  "update value"
  (str (blk-send (doc-find d3 "p1") "text"))
  "Edited")
(content-test "update keeps order" (doc-ids d3) (list "h" "p1" "p2"))
(content-test
  "update immutable"
  (str (blk-send (doc-find d1 "p1") "text"))
  "First")

;; ── delete ──
(define d4 (doc-delete d1 "p1"))
(content-test "delete order" (doc-ids d4) (list "h" "p2"))
(content-test "delete count" (doc-count d4) 2)
(content-test "delete immutable" (doc-count d1) 3)
(content-test
  "delete missing no-op"
  (doc-ids (doc-delete d1 "x"))
  (list "h" "p1" "p2"))

;; ── move ──
(content-test
  "move p2 to front"
  (doc-ids (doc-move d1 "p2" 0))
  (list "p2" "h" "p1"))
(content-test
  "move h to end"
  (doc-ids (doc-move d1 "h" 2))
  (list "p1" "p2" "h"))
(content-test
  "move missing no-op"
  (doc-ids (doc-move d1 "x" 0))
  (list "h" "p1" "p2"))
(content-test "move immutable" (doc-ids d1) (list "h" "p1" "p2"))

;; ── op constructors + interpreter ──
(content-test
  "op-insert apply"
  (doc-ids (doc-apply d1 (op-insert img "h")))
  (list "h" "img" "p1" "p2"))
(content-test
  "op-delete apply"
  (doc-ids (doc-apply d1 (op-delete "h")))
  (list "p1" "p2"))
(content-test
  "op-move apply"
  (doc-ids (doc-apply d1 (op-move "p2" 0)))
  (list "p2" "h" "p1"))
(content-test
  "op-update apply"
  (str
    (blk-send
      (doc-find (doc-apply d1 (op-update "p1" "text" "X")) "p1")
      "text"))
  "X")

;; ── apply-all: a stream of ops ──
(define
  ops
  (list (op-insert img "h") (op-delete "p1") (op-move "p2" 0)))
(content-test
  "apply-all"
  (doc-ids (doc-apply-all d1 ops))
  (list "p2" "h" "img"))
(content-test "apply-all immutable" (doc-ids d1) (list "h" "p1" "p2"))
(content-test
  "apply-all empty"
  (doc-ids (doc-apply-all d1 (list)))
  (list "h" "p1" "p2"))
