(import (sx dom))
(import (sx browser))

(define-library (sx signals-web)
  (export with-marsh-scope dispose-marsh-scope emit-event on-event bridge-event resource)
  (begin

;; ==========================================================================
;; web/signals.sx — Web platform signal extensions
;;
;; Extends the core reactive signal spec (spec/signals.sx) with web-specific
;; features: marsh scopes (DOM lifecycle), named stores (page-level state),
;; event bridge (lake→island communication), and async resources.
;;
;; These depend on platform primitives:
;;   dom-set-data, dom-get-data, dom-listen, dom-dispatch, event-detail,
;;   promise-then
;; ==========================================================================


;; --------------------------------------------------------------------------
;; Marsh scopes — child scopes within islands
;; --------------------------------------------------------------------------

(define with-marsh-scope :effects [mutation io]
  (fn (marsh-el (body-fn :as lambda))
    (let ((disposers (list)))
      (with-island-scope
        (fn (d) (append! disposers d))
        body-fn)
      (dom-set-data marsh-el "sx-marsh-disposers" disposers))))

(define dispose-marsh-scope :effects [mutation io]
  (fn (marsh-el)
    (let ((disposers (dom-get-data marsh-el "sx-marsh-disposers")))
      (when disposers
        (for-each (fn ((d :as lambda)) (cek-call d nil)) disposers)
        (dom-set-data marsh-el "sx-marsh-disposers" nil)))))


;; --------------------------------------------------------------------------
;; Named stores — page-level signal containers
;; --------------------------------------------------------------------------

;; def-store, use-store, clear-stores are now OCaml primitives
;; (sx_primitives.ml) with a global mutable registry that survives
;; env scoping across bytecode modules and island hydration.


;; --------------------------------------------------------------------------
;; Event bridge — DOM event communication for lake→island
;; --------------------------------------------------------------------------

(define emit-event :effects [io]
  (fn (el (event-name :as string) detail)
    (dom-dispatch el event-name detail)))

(define on-event :effects [io]
  (fn (el (event-name :as string) (handler :as lambda))
    (dom-on el event-name handler)))

(define bridge-event :effects [mutation io]
  (fn (el (event-name :as string) (target-signal :as signal) transform-fn)
    (effect (fn ()
      (let ((remove (dom-on el event-name
              (fn (e)
                (let ((detail (event-detail e))
                      (new-val (if transform-fn
                                 (cek-call transform-fn (list detail))
                                 detail)))
                  (reset! target-signal new-val))))))
        remove)))))


;; --------------------------------------------------------------------------
;; Resource — async signal with loading/resolved/error states
;; --------------------------------------------------------------------------

(define resource :effects [mutation io]
  (fn ((fetch-fn :as lambda))
    (let ((state (signal (dict "loading" true "data" nil "error" nil))))
      (promise-then
        (cek-call fetch-fn nil)
        (fn (data)
          (reset! state (dict "loading" false "data" data "error" nil)))
        (fn (err)
          (reset! state (dict "loading" false "data" nil "error" err))))
      state)))


))

;; Re-export to global env
(import (sx signals-web))
