;; ---------------------------------------------------------------------------
;; L3: Commands with History
;; ---------------------------------------------------------------------------
(defcomp ()
  (~docs/page :title "Layer 3: Commands with History"

    (p (~tw :tokens "text-stone-500 text-sm italic mb-8")
      "Command pattern built into signal stores. Commands are s-expressions "
      "in a history stack — trivially serializable, with transaction grouping for drag operations.")

    (~docs/section :title "Live Demo" :id "demo"
      (p "A counter with full undo/redo. Each operation pushes the previous state onto the undo stack. Undo pops from undo and pushes to redo. All state is reactive — buttons disable when their stack is empty.")
      (~reactive-runtime/demo-commands)
      (~docs/code :src (highlight "(defisland ~demo-commands ()\n  (let ((value (signal 0))\n        (undo-stack (signal (list)))\n        (redo-stack (signal (list)))\n        (do-cmd (fn (new-val)\n          (batch (fn ()\n            (swap! undo-stack (fn (s) (cons (deref value) s)))\n            (reset! redo-stack (list))\n            (reset! value new-val)))))\n        (undo (fn ()\n          (when (> (len (deref undo-stack)) 0)\n            (batch (fn ()\n              (let ((prev (first (deref undo-stack))))\n                (swap! redo-stack (fn (r) (cons (deref value) r)))\n                (reset! value prev)\n                (swap! undo-stack rest)))))))\n        (redo (fn () ...)))\n    (span (deref value))\n    (button :on-click (fn (e) (do-cmd (+ (deref value) 1))) \"+1\")\n    (button :on-click (fn (e) (undo)) \"Undo\")))" "lisp"))
      (p "Three signals and two functions. " (code "batch") " groups the three writes (push old, clear redo, set new) into one notification pass. The stacks use " (code "cons") "/" (code "first") "/" (code "rest") " — standard list operations. " (code "make-command-store") " wraps this pattern and adds transaction grouping."))

    (~docs/section :title "API" :id "commands-api"

      (~docs/code :src (highlight
        "(define canvas-state (make-command-store\n  {:initial {:elements (list) :selection nil}\n   :commands {:add-element (fn (state el)\n                (assoc state :elements\n                  (append (get state :elements) (list el))))\n              :move-element (fn (state id dx dy) ...)}\n   :max-history 100}))\n\n;; Dispatch commands\n(cmd-dispatch! canvas-state :add-element rect-1)\n\n;; Undo / redo\n(cmd-undo! canvas-state)\n(cmd-redo! canvas-state)\n\n;; Reactive predicates\n(deref (cmd-can-undo? canvas-state))  ;; => true\n(deref (cmd-can-redo? canvas-state))  ;; => false\n\n;; Transaction grouping — collapses into single undo entry\n(cmd-group-start! canvas-state \"drag\")\n(cmd-dispatch! canvas-state :move-element id 1 0)\n(cmd-dispatch! canvas-state :move-element id 1 0)\n(cmd-dispatch! canvas-state :move-element id 1 0)\n(cmd-group-end! canvas-state)\n;; One undo reverses all three moves"
        "lisp")))

    (~docs/section :title "Internals" :id "commands-internals"

      (p "A command store wraps three signals and a ref:")
      (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
        (li (strong "state") " signal — current application state")
        (li (strong "undo-stack") " signal — list of previous states")
        (li (strong "redo-stack") " signal — list of undone states")
        (li (strong "group") " ref — current transaction label (nil when not grouping)"))

      (p (code "cmd-dispatch!") " applies the command function to the current state, "
         "pushes the old state onto the undo stack, and clears the redo stack. "
         "During a group, intermediate states are collapsed so only the pre-group "
         "state appears on the undo stack.")

      (p "~320 lines. The most complex layer, but pure signals + dicts."))))
