;; Deref as Shift — CEK-Based Reactive DOM Renderer
;; Phase B: replace explicit effects with implicit continuation capture.

(defcomp ()
  (~docs/page :title "Deref as Shift — CEK-Based Reactive DOM Renderer"

    (p (~tw :tokens "text-stone-500 text-sm italic mb-8")
      "Phase A collapsed signals to plain dicts with zero platform primitives. "
      "Phase B replaces explicit effect wrapping in the reactive DOM renderer "
      "with implicit continuation capture: when " (code "deref") " encounters a signal "
      "inside a " (code "reactive-reset") " boundary, it performs " (code "shift") ", "
      "capturing the rest of the expression as a continuation. "
      "That continuation IS the subscriber.")

    ;; =====================================================================
    ;; The Insight
    ;; =====================================================================

    (~docs/section :title "The Insight" :id "insight"

      (p "Each reactive binding is a micro-computation:")
      (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
        (li (code "reactive-text") ": given signal value, set text node content to " (code "(str value)"))
        (li (code "reactive-attr") ": given signal value, set attribute to " (code "(str value)")))

      (p "Currently wrapped in explicit " (code "effect") " calls. With deref-as-shift, "
         "the continuation captures this automatically:")

      (~docs/code :src (highlight
        ";; User writes:\n(div :class (str \"count-\" (deref counter))\n  (str \"Value: \" (deref counter)))\n\n;; Renderer internally wraps each expression:\n(div :class (reactive-reset update-attr-fn (str \"count-\" (deref counter)))\n  (reactive-reset update-text-fn (str \"Value: \" (deref counter))))\n\n;; When (deref counter) hits a signal inside reactive-reset:\n;;   1. Shift: capture continuation (str \"count-\" [HOLE])\n;;   2. Register continuation as signal subscriber\n;;   3. Return current value for initial render\n;; When counter changes:\n;;   Re-invoke continuation with new value → update-fn updates DOM"
        "lisp")))

    ;; =====================================================================
    ;; Step 1: Bootstrap CEK to JavaScript
    ;; =====================================================================

    (~docs/section :title "Step 1: Bootstrap CEK to JavaScript" :id "step-1"

      (p "Add " (code "frames.sx") " + " (code "cek.sx") " to the JS build pipeline. "
         "Currently CEK is Python-only.")

      (~docs/subsection :title "1a. platform_js.py — SPEC_MODULES + platform code"

        (p "Add to " (code "SPEC_MODULES") " dict:")
        (~docs/code :src (highlight
          "\"frames\": (\"frames.sx\", \"frames (CEK continuation frames)\"),\n\"cek\": (\"cek.sx\", \"cek (explicit CEK machine evaluator)\"),\n\n# Add ordering (new constant):\nSPEC_MODULE_ORDER = [\"deps\", \"frames\", \"page-helpers\", \"router\", \"signals\", \"cek\"]"
          "python"))

        (p "Add " (code "PLATFORM_CEK_JS") " constant (mirrors " (code "PLATFORM_CEK_PY") "):")
        (~docs/code :src (highlight
          "// Primitive aliases used by cek.sx\nvar inc = PRIMITIVES[\"inc\"];\nvar dec = PRIMITIVES[\"dec\"];\nvar zip_pairs = PRIMITIVES[\"zip-pairs\"];\n\nfunction makeCekContinuation(captured, restKont) {\n  var c = new Continuation(function(v) { return v !== undefined ? v : NIL; });\n  c._cek_data = {\"captured\": captured, \"rest-kont\": restKont};\n  return c;\n}\nfunction continuationData(c) {\n  return (c && c._cek_data) ? c._cek_data : {};\n}"
          "javascript"))

        (p "Add " (code "CEK_FIXUPS_JS") " — iterative " (code "cek-run") " override:")
        (~docs/code :src (highlight
          "cekRun = function(state) {\n  while (!cekTerminal_p(state)) { state = cekStep(state); }\n  return cekValue(state);\n};"
          "javascript")))

      (~docs/subsection :title "1b. run_js_sx.py — Update compile_ref_to_js"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li "Auto-add " (code "\"frames\"") " when " (code "\"cek\"") " in spec_mod_set (mirror Python " (code "bootstrap_py.py") ")")
          (li "Auto-add " (code "\"cek\"") " + " (code "\"frames\"") " when " (code "\"dom\"") " adapter included (CEK needed for reactive rendering)")
          (li "Use " (code "SPEC_MODULE_ORDER") " for ordering instead of " (code "sorted()"))
          (li "Add " (code "has_cek") " flag")
          (li "Include " (code "PLATFORM_CEK_JS") " after transpiled code when " (code "has_cek"))
          (li "Include " (code "CEK_FIXUPS_JS") " in fixups section when " (code "has_cek"))))

      (~docs/subsection :title "1c. js.sx — RENAMES for predicate functions"
        (p "Default mangling handles most names. Only add RENAMES where " (code "?")
           " suffix needs clean JS names:")
        (~docs/code :src (highlight
          "\"cek-terminal?\" \"cekTerminalP\"\n\"kont-empty?\" \"kontEmptyP\"\n\"make-cek-continuation\" \"makeCekContinuation\"\n\"continuation-data\" \"continuationData\""
          "lisp")))

      (~docs/subsection :title "1d. bootstrap_py.py — RENAMES for CEK predicates"
        (~docs/code :src (highlight
          "\"cek-terminal?\": \"cek_terminal_p\",\n\"kont-empty?\": \"kont_empty_p\",\n\"make-cek-continuation\": \"make_cek_continuation\",\n\"continuation-data\": \"continuation_data\","
          "python")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li "Rebootstrap JS: " (code "python3 bootstrap_js.py"))
          (li "Check output contains frame constructors + CEK step functions")
          (li "Run existing CEK Python tests: " (code "python3 run_cek_tests.py") " (should still pass)"))))

    ;; =====================================================================
    ;; Step 2: ReactiveResetFrame + DerefFrame
    ;; =====================================================================

    (~docs/section :title "Step 2: ReactiveResetFrame + DerefFrame" :id "step-2"

      (p "New frame types in " (code "frames.sx") " that enable deref-as-shift.")

      (~docs/subsection :title "2a. New frame constructors"
        (~docs/code :src (highlight
          ";; ReactiveResetFrame: delimiter for reactive deref-as-shift\n;; Carries an update-fn that gets called with new values on re-render.\n(define make-reactive-reset-frame\n  (fn (env update-fn first-render?)\n    {:type \"reactive-reset\" :env env :update-fn update-fn\n     :first-render first-render?}))\n\n;; DerefFrame: awaiting evaluation of deref's argument\n(define make-deref-frame\n  (fn (env)\n    {:type \"deref\" :env env}))"
          "lisp")))

      (~docs/subsection :title "2b. Update kont-capture-to-reset"
        (p "Must stop at EITHER " (code "\"reset\"") " OR " (code "\"reactive-reset\"") ":")
        (~docs/code :src (highlight
          "(define kont-capture-to-reset\n  (fn (kont)\n    (define scan\n      (fn (k captured)\n        (if (empty? k)\n          (error \"shift without enclosing reset\")\n          (let ((frame (first k)))\n            (if (or (= (frame-type frame) \"reset\")\n                    (= (frame-type frame) \"reactive-reset\"))\n              (list captured (rest k))\n              (scan (rest k) (append captured (list frame))))))))\n    (scan kont (list))))"
          "lisp")))

      (~docs/subsection :title "2c. Helpers to scan for ReactiveResetFrame"
        (~docs/code :src (highlight
          "(define has-reactive-reset-frame?\n  (fn (kont)\n    (if (empty? kont) false\n      (if (= (frame-type (first kont)) \"reactive-reset\") true\n        (has-reactive-reset-frame? (rest kont))))))\n\n;; Returns 3 values: (captured, frame, rest)\n(define kont-capture-to-reactive-reset\n  (fn (kont)\n    (define scan\n      (fn (k captured)\n        (if (empty? k)\n          (error \"reactive deref without enclosing reactive-reset\")\n          (let ((frame (first k)))\n            (if (= (frame-type frame) \"reactive-reset\")\n              (list captured frame (rest k))\n              (scan (rest k) (append captured (list frame))))))))\n    (scan kont (list))))"
          "lisp"))))

    ;; =====================================================================
    ;; Step 3: Make deref a CEK Special Form
    ;; =====================================================================

    (~docs/section :title "Step 3: Make deref a CEK Special Form" :id "step-3"

      (p "When " (code "deref") " encounters a signal inside a " (code "reactive-reset")
         ", perform shift.")

      (~docs/subsection :title "3a. Add to special form dispatch in cek.sx"
        (p "In the dispatch table (around where " (code "reset") " and " (code "shift") " are):")
        (~docs/code :src (highlight
          "(= name \"deref\") (step-sf-deref args env kont)"
          "lisp")))

      (~docs/subsection :title "3b. step-sf-deref"
        (p "Evaluates the argument first (push DerefFrame), then decides whether to shift:")
        (~docs/code :src (highlight
          "(define step-sf-deref\n  (fn (args env kont)\n    (make-cek-state\n      (first args) env\n      (kont-push (make-deref-frame env) kont))))"
          "lisp")))

      (~docs/subsection :title "3c. Handle DerefFrame in step-continue"
        (p "When the deref argument is evaluated, decide: shift or return.")
        (~docs/code :src (highlight
          "(= ft \"deref\")\n  (let ((val value)\n        (fenv (get frame \"env\")))\n    (if (not (signal? val))\n      ;; Not a signal: pass through\n      (make-cek-value val fenv rest-k)\n      ;; Signal: check for ReactiveResetFrame\n      (if (has-reactive-reset-frame? rest-k)\n        ;; Perform reactive shift\n        (reactive-shift-deref val fenv rest-k)\n        ;; No reactive-reset: normal deref (scope-based tracking)\n        (do\n          (let ((ctx (context \"sx-reactive\" nil)))\n            (when ctx\n              (let ((dep-list (get ctx \"deps\"))\n                    (notify-fn (get ctx \"notify\")))\n                (when (not (contains? dep-list val))\n                  (append! dep-list val)\n                  (signal-add-sub! val notify-fn)))))\n          (make-cek-value (signal-value val) fenv rest-k)))))"
          "lisp")))

      (~docs/subsection :title "3d. reactive-shift-deref — the heart"
        (~docs/code :src (highlight
          "(define reactive-shift-deref\n  (fn (sig env kont)\n    (let ((scan-result (kont-capture-to-reactive-reset kont))\n          (captured-frames (first scan-result))\n          (reset-frame (nth scan-result 1))\n          (remaining-kont (nth scan-result 2))\n          (update-fn (get reset-frame \"update-fn\")))\n      ;; Sub-scope for nested subscriber cleanup on re-invocation\n      (let ((sub-disposers (list)))\n        (let ((subscriber\n                (fn ()\n                  ;; Dispose previous nested subscribers\n                  (for-each (fn (d) (invoke d)) sub-disposers)\n                  (set! sub-disposers (list))\n                  ;; Re-invoke: push fresh ReactiveResetFrame (first-render=false)\n                  (let ((new-reset (make-reactive-reset-frame env update-fn false))\n                        (new-kont (concat captured-frames\n                                          (list new-reset)\n                                          remaining-kont)))\n                    (with-island-scope\n                      (fn (d) (append! sub-disposers d))\n                      (fn ()\n                        (cek-run\n                          (make-cek-value (signal-value sig) env new-kont))))))))\n          ;; Register subscriber\n          (signal-add-sub! sig subscriber)\n          ;; Register cleanup with island scope\n          (register-in-scope\n            (fn ()\n              (signal-remove-sub! sig subscriber)\n              (for-each (fn (d) (invoke d)) sub-disposers)))\n          ;; Return current value for initial render\n          (make-cek-value (signal-value sig) env remaining-kont))))))"
          "lisp")))

      (~docs/subsection :title "3e. Handle ReactiveResetFrame in step-continue"
        (p "When expression completes normally (or after re-invocation):")
        (~docs/code :src (highlight
          "(= ft \"reactive-reset\")\n  (let ((update-fn (get frame \"update-fn\"))\n        (first? (get frame \"first-render\")))\n    ;; On re-render (not first), call update-fn with new value\n    (when (and update-fn (not first?))\n      (invoke update-fn value))\n    (make-cek-value value env rest-k))"
          "lisp"))
        (p (strong "Key:") " On first render, update-fn is NOT called — the value flows back to the caller "
           "who inserts it into the DOM. On re-render (subscriber fires), update-fn IS called "
           "to mutate the existing DOM.")))

    ;; =====================================================================
    ;; Step 4: Integrate into adapter-dom.sx
    ;; =====================================================================

    (~docs/section :title "Step 4: Integrate into adapter-dom.sx" :id "step-4"

      (p "Add CEK reactive path alongside existing effect-based path, controlled by opt-in flag.")

      (~docs/subsection :title "4a. Opt-in flag"
        (~docs/code :src (highlight
          "(define *use-cek-reactive* false)\n(define enable-cek-reactive! (fn () (set! *use-cek-reactive* true)))"
          "lisp")))

      (~docs/subsection :title "4b. CEK reactive attribute binding"
        (~docs/code :src (highlight
          "(define cek-reactive-attr\n  (fn (el attr-name expr env)\n    (let ((update-fn (fn (val)\n            (cond\n              (or (nil? val) (= val false)) (dom-remove-attr el attr-name)\n              (= val true) (dom-set-attr el attr-name \"\")\n              :else (dom-set-attr el attr-name (str val))))))\n      ;; Mark for morph protection\n      (let ((existing (or (dom-get-attr el \"data-sx-reactive-attrs\") \"\"))\n            (updated (if (empty? existing) attr-name (str existing \",\" attr-name))))\n        (dom-set-attr el \"data-sx-reactive-attrs\" updated))\n      ;; Initial render via CEK with ReactiveResetFrame\n      (let ((initial (cek-run\n                       (make-cek-state expr env\n                         (list (make-reactive-reset-frame env update-fn true))))))\n        (invoke update-fn initial)))))"
          "lisp")))

      (~docs/subsection :title "4c. Modify render-dom-element dispatch"
        (p "In attribute processing, add conditional:")
        (~docs/code :src (highlight
          "(context \"sx-island-scope\" nil)\n  (if *use-cek-reactive*\n    (cek-reactive-attr el attr-name attr-expr env)\n    (reactive-attr el attr-name\n      (fn () (trampoline (eval-expr attr-expr env)))))"
          "lisp"))
        (p "Similarly for text positions and conditional rendering."))

      (~docs/subsection :title "4d. CEK reactive text"
        (~docs/code :src (highlight
          "(define cek-reactive-text\n  (fn (expr env)\n    (let ((node (create-text-node \"\"))\n          (update-fn (fn (val)\n                       (dom-set-text-content node (str val)))))\n      (let ((initial (cek-run\n                       (make-cek-state expr env\n                         (list (make-reactive-reset-frame env update-fn true))))))\n        (dom-set-text-content node (str initial))\n        node))))"
          "lisp")))

      (~docs/subsection :title "4e. What stays unchanged"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "reactive-list") " — keyed reconciliation is complex; keep effect-based for now")
          (li (code "reactive-spread") " — spread tracking is complex; keep effect-based")
          (li (code "effect") ", " (code "computed") " — still needed for non-rendering side effects")
          (li "Existing " (code "reactive-*") " functions — remain as default path"))))

    ;; =====================================================================
    ;; Step 5: Tests
    ;; =====================================================================

    (~docs/section :title "Step 5: Tests" :id "step-5"

      (~docs/subsection :title "5a. test-cek-reactive.sx"
        (p "Tests:")
        (ol (~tw :tokens "list-decimal pl-6 mb-4 space-y-1")
          (li (code "deref") " non-signal passes through (no shift)")
          (li (code "deref") " signal without reactive-reset: returns value, no subscription")
          (li (code "deref") " signal with reactive-reset: shifts, registers subscriber, update-fn called on change")
          (li "Expression with deref: " (code "(str \"hello \" (deref sig))") " — continuation captures rest")
          (li "Multi-deref: both signals create subscribers, both fire correctly")
          (li "Disposal: removing island scope unsubscribes all continuations")
          (li "Stale subscriber cleanup: re-invocation disposes nested subscribers")))

      (~docs/subsection :title "5b. run_cek_reactive_tests.py"
        (p "Mirrors " (code "run_cek_tests.py") ". "
           "Loads frames.sx, cek.sx, signals.sx, runs test-cek-reactive.sx.")))

    ;; =====================================================================
    ;; Step 6: Browser Demo
    ;; =====================================================================

    (~docs/section :title "Step 6: Browser Demo" :id "step-6"
      (p "Demo showing:")
      (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
        (li "Counter island with implicit reactivity (no explicit effects)")
        (li (code "(deref counter)") " in text position auto-updates")
        (li (code "(str \"count-\" (deref class-sig))") " in attr position auto-updates")
        (li "Side-by-side comparison: effect-based vs continuation-based code")))

    ;; =====================================================================
    ;; Multi-Deref Handling
    ;; =====================================================================

    (~docs/section :title "Multi-Deref Handling" :id "multi-deref"

      (~docs/code :src (highlight "(str (deref first-name) \" \" (deref last-name))" "lisp"))

      (ol (~tw :tokens "list-decimal pl-6 mb-6 space-y-3")
        (li (strong "Initial render:") " First " (code "deref") " hits signal → shifts, captures "
            (code "(str [HOLE] \" \" (deref last-name))") ". Subscriber registered for "
            (code "first-name") ". Returns current value. Second " (code "deref")
            " runs (no ReactiveResetFrame between it and the already-consumed one) — "
            "falls through to normal scope-based tracking.")
        (li (strong "first-name changes:") " Subscriber fires → re-pushes ReactiveResetFrame → "
            "re-invokes continuation with new first-name value → second " (code "deref")
            " hits ReactiveResetFrame again → shifts, creates NEW subscriber for "
            (code "last-name") ". Old last-name subscriber cleaned up via sub-scope disposal.")
        (li (strong "last-name changes:") " Its subscriber fires → re-invokes inner continuation → "
            "update-fn called with new result."))

      (p "This creates O(n) nested continuations for n derefs. Fine for small reactive expressions."))

    ;; =====================================================================
    ;; Commit Strategy
    ;; =====================================================================

    (~docs/section :title "Commit Strategy" :id "commits"

      (ol (~tw :tokens "list-decimal pl-6 mb-4 space-y-1")
        (li (strong "Commit 1:") " Bootstrap CEK to JS (Step 1) — mechanical, independent")
        (li (strong "Commit 2:") " ReactiveResetFrame + DerefFrame (Step 2) — new frame types")
        (li (strong "Commit 3:") " Deref-as-shift + adapter integration + tests (Steps 3-5) — the core change")
        (li (strong "Commit 4:") " Browser demo (Step 6)")))

    ;; =====================================================================
    ;; Files
    ;; =====================================================================

    (~docs/section :title "Files" :id "files"

      (div (~tw :tokens "overflow-x-auto mb-6")
        (table (~tw :tokens "min-w-full text-sm")
          (thead (tr
            (th (~tw :tokens "text-left pr-4 pb-2 font-semibold") "File")
            (th (~tw :tokens "text-left pb-2 font-semibold") "Change")))
          (tbody
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/platform_js.py")
                (td "SPEC_MODULES entries, PLATFORM_CEK_JS, CEK_FIXUPS_JS, SPEC_MODULE_ORDER"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/run_js_sx.py")
                (td "compile_ref_to_js: has_cek, auto-inclusion, ordering, platform code"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/js.sx")
                (td "RENAMES for CEK predicate functions"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/bootstrap_py.py")
                (td "RENAMES for CEK predicates"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/frames.sx")
                (td "ReactiveResetFrame, DerefFrame, has-reactive-reset-frame?, kont-capture-to-reactive-reset"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/cek.sx")
                (td "step-sf-deref, reactive-shift-deref, deref in dispatch, ReactiveResetFrame in step-continue"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/adapter-dom.sx")
                (td "*use-cek-reactive* flag, cek-reactive-attr, cek-reactive-text, conditional dispatch"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/test-cek-reactive.sx")
                (td (strong "New:") " continuation-based reactivity tests"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/run_cek_reactive_tests.py")
                (td (strong "New:") " Python test runner"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/sx/ref/sx_ref.py")
                (td "Rebootstrap (generated)"))
            (tr (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "shared/static/scripts/sx-browser.js")
                (td "Rebootstrap (generated)"))))))

    ;; =====================================================================
    ;; Risks
    ;; =====================================================================

    (~docs/section :title "Risks" :id "risks"

      (ol (~tw :tokens "list-decimal pl-6 mb-4 space-y-2")
        (li (strong "Performance:") " CEK allocates a dict per step. Mitigated: opt-in flag, tree-walk remains default.")
        (li (strong "Multi-deref stale subscribers:") " Mitigated: sub-scope disposal before re-invocation.")
        (li (strong "Interaction with user shift/reset:") " " (code "kont-capture-to-reactive-reset")
            " only scans for " (code "\"reactive-reset\"") ", not " (code "\"reset\"") ". Orthogonal.")
        (li (strong "JS bootstrapper complexity:") " ~10 RENAMES for predicates. Default mangling handles the rest.")))))
