;; _hyperscript integration — wire _="..." attributes to compiled SX
;;
;; Entry points:
;;   (hs-handler src)       — compile source to callable (fn (me) ...)
;;   (hs-activate! el)      — activate hyperscript on a single element
;;   (hs-boot!)             — scan DOM, activate all _="..." elements
;;   (hs-boot-subtree! root) — activate within a subtree (for HTMX swaps)

;; ── Compile source to a handler function ────────────────────────
;; Returns a function (fn (me) ...) that can be called with a DOM element.
;; Uses eval-expr-cek to turn the SX data structure into a live closure.

(begin
  (define
    hs-collect-vars
    (fn
      (sx)
      (define vars (list))
      (define
        reserved
        (list
          (quote beingTold)
          (quote me)
          (quote it)
          (quote event)
          (quote you)
          (quote yourself)))
      (define
        walk
        (fn
          (node)
          (when
            (list? node)
            (when
              (and
                (> (len node) 1)
                (= (first node) (quote set!))
                (symbol? (nth node 1)))
              (let
                ((name (nth node 1)))
                (when
                  (and
                    (not (some (fn (v) (= v name)) vars))
                    (not (some (fn (v) (= v name)) reserved)))
                  (set! vars (cons name vars)))))
            (for-each walk node))))
      (walk sx)
      vars))
  (define
    hs-handler
    (fn
      (src)
      (let
        ((sx (hs-to-sx-from-source src)))
        (let
          ((extra-vars (hs-collect-vars sx)))
          (do
            (for-each
              (fn (v) (eval-expr-cek (list (quote define) v nil)))
              extra-vars)
            (let
              ((guarded (list (quote guard) (list (quote _e) (list (quote true) (list (quote if) (list (quote and) (list (quote list?) (quote _e)) (list (quote =) (list (quote first) (quote _e)) "hs-return")) (list (quote nth) (quote _e) 1) (list (quote raise) (quote _e))))) sx)))
              (eval-expr-cek
                (list
                  (quote fn)
                  (list (quote me))
                  (list
                    (quote let)
                    (list
                      (list (quote beingTold) (quote me))
                      (list (quote it) nil)
                      (list (quote event) nil))
                    guarded))))))))))

;; ── Activate a single element ───────────────────────────────────
;; Reads the _="..." attribute, compiles, and executes with me=element.
;; Marks the element to avoid double-activation.

(define
  hs-register-scripts!
  (fn
    ()
    (for-each
      (fn
        (script)
        (when
          (not (dom-get-data script "hs-script-loaded"))
          (let
            ((src (host-get script "innerHTML")))
            (guard
              (_e (true nil))
              (let
                ((handler (eval-expr-cek (hs-to-sx-from-source src))))
                (handler (dom-body)))))))
      (hs-query-all "script[type=text/hyperscript]"))))

;; ── Boot: scan entire document ──────────────────────────────────
;; Called once at page load. Finds all elements with _ attribute,
;; compiles their hyperscript, and activates them.

(define
  hs-scripting-disabled?
  (fn
    (el)
    (if
      (= el nil)
      false
      (if
        (dom-get-attr el "disable-scripting")
        true
        (hs-scripting-disabled? (dom-parent el))))))

;; ── Boot subtree: for dynamic content ───────────────────────────
;; Called after HTMX swaps or dynamic DOM insertion.
;; Only activates elements within the given root.

(define
  hs-activate!
  (fn
    (el)
    (do
      (hs-register-scripts!)
      (let
        ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script")))
        (when
          (and src (not (= src prev)) (not (hs-scripting-disabled? el)))
          (when
            (dom-dispatch el "hyperscript:before:init" nil)
            (hs-log-event! "hyperscript:init")
            (dom-set-data el "hs-script" src)
            (dom-set-data el "hs-active" true)
            (dom-set-attr el "data-hyperscript-powered" "true")
            (host-set! (host-global "window") "__hs_current_me" el)
            (guard
              (_e
                (true
                  (do
                    (dom-dispatch el "hyperscript:parse-error" {:errors (list _e)})
                    nil)))
              (let
                ((handler (hs-handler src)))
                (let
                  ((el-type (dom-get-attr el "type"))
                    (comp-name (dom-get-attr el "component")))
                  (let
                    ((safe-handler (fn (e) (host-call-fn handler (list e)))))
                    (if
                      (= el-type "text/hyperscript-template")
                      (for-each
                        safe-handler
                        (hs-query-all (or comp-name "")))
                      (safe-handler el))))))
            (host-set! (host-global "window") "__hs_current_me" nil)
            (dom-dispatch el "hyperscript:after:init" nil)))))))

(define
  hs-deactivate!
  (fn
    (el)
    (let
      ((unlisteners (or (dom-get-data el "hs-unlisteners") (list))))
      (for-each (fn (u) (when u (u))) unlisteners)
      (dom-set-data el "hs-unlisteners" (list))
      (dom-set-data el "hs-active" false)
      (dom-set-data el "hs-script" nil))))

(define
  hs-boot!
  (fn
    ()
    (let
      ((elements (dom-query-all (host-get (host-global "document") "body") "[_]")))
      (for-each (fn (el) (hs-activate! el)) elements))))

(define
  hs-boot-subtree!
  (fn
    (root)
    (let
      ((elements (dom-query-all root "[_]")))
      (for-each (fn (el) (hs-activate! el)) elements))
    (when (dom-get-attr root "_") (hs-activate! root))))