(defcomp
  ()
  (~docs/page
    :title "Event Bridge"
    (~docs/section
      :title "The Problem"
      :id "problem"
      (p
        "A reactive island can contain server-rendered content — an htmx \"lake\" that swaps via "
        (code "sx-get")
        "/"
        (code "sx-post")
        ". The lake content is pure HTML from the server. It has no access to island signals.")
      (p
        "But sometimes the lake needs to "
        (em "tell")
        " the island something happened. A server-rendered \"Add to Cart\" button needs to update the island's cart signal. A server-rendered search form needs to feed results into the island's result signal.")
      (p
        "The event bridge solves this: DOM custom events bubble from the lake up to the island, where an effect listens and updates signals."))
    (~docs/section
      :title "How it works"
      :id "how"
      (p "Three components:")
      (ol
        (~tw :tokens "space-y-2 text-stone-600 list-decimal list-inside")
        (li
          (strong "Server emits: ")
          "Server-rendered elements carry "
          (code "data-sx-emit")
          " attributes. When the user interacts, the client dispatches a CustomEvent.")
        (li
          (strong "Event bubbles: ")
          "The event bubbles up through the DOM tree until it reaches the island container.")
        (li
          (strong "Effect catches: ")
          "An effect inside the island listens for the event name and updates a signal."))
      (~docs/code
        :src (highlight
          ";; Island with an event bridge\n(defisland ~reactive-islands/event-bridge/product-page (&key product)\n  (let ((cart-items (signal (list))))\n\n    ;; Bridge: listen for \"cart:add\" events from server content\n    (bridge-event container \"cart:add\" cart-items\n      (fn (detail)\n        (append (deref cart-items)\n                (dict :id (get detail \"id\")\n                      :name (get detail \"name\")\n                      :price (get detail \"price\")))))\n\n    (div\n      ;; Island header with reactive cart count\n      (div :class \"flex justify-between\"\n        (h1 (get product \"name\"))\n        (span :class \"badge\" (length (deref cart-items)) \" items\"))\n\n      ;; htmx lake — server-rendered product details\n      ;; This content is swapped by sx-get, not rendered by the island\n      (div :id \"product-details\"\n        :sx-get (str \"/products/\" (get product \"id\") \"/details\")\n        :sx-swap \"innerHTML\"\n        :sx-trigger \"load\"))))"
          "lisp"))
      (p
        "The server handler for "
        (code "/products/:id/details")
        " returns HTML with emit attributes:")
      (~docs/code
        :src (highlight
          ";; Server-rendered response (pure HTML, no signals)\n(div\n  (p (get product \"description\"))\n  (div :class \"flex gap-2 mt-4\"\n    (button\n      :data-sx-emit \"cart:add\"\n      :data-sx-emit-detail (json-serialize\n        (dict :id (get product \"id\")\n              :name (get product \"name\")\n              :price (get product \"price\")))\n      :class \"bg-violet-600 text-white px-4 py-2 rounded\"\n      \"Add to Cart\")))"
          "lisp"))
      (p
        "The button is plain server HTML. When clicked, the client's event bridge dispatches "
        (code "cart:add")
        " with the JSON detail. The island effect catches it and appends to "
        (code "cart-items")
        ". The badge updates reactively."))
    (~docs/section
      :title "Why signals survive swaps"
      :id "survival"
      (p
        "Signals live in JavaScript memory (closures), not in the DOM. When htmx swaps content inside an island:")
      (ul
        (~tw :tokens "space-y-2 text-stone-600 list-disc pl-5")
        (li
          (strong "Swap inside island: ")
          "Signals survive. The lake content is replaced but the island's signal closures are untouched. Effects re-bind to new DOM nodes if needed.")
        (li
          (strong "Swap outside island: ")
          "Signals survive. The island is not affected by swaps to other parts of the page.")
        (li
          (strong "Swap replaces island: ")
          "Signals are "
          (em "lost")
          ". The island is disposed. This is where "
          (a
            :href "/sx/(geography.(reactive.(named-stores)))"
            :sx-get "/sx/(geography.(reactive.(named-stores)))"
            :sx-target "#sx-content"
            :sx-select "#sx-content"
            :sx-swap "outerHTML"
            :sx-push-url "true"
            (~tw :tokens "text-violet-700 underline")
            "named stores")
          " come in — they persist at page level, surviving island destruction.")))
    (~docs/section
      :title "Spec"
      :id "spec"
      (p
        "The event bridge is spec'd in "
        (code "signals.sx")
        " (sections 12-13). Three functions:")
      (~docs/code
        :src (highlight
          ";; Low-level: dispatch a custom event\n(emit-event el \"cart:add\" {:id 42 :name \"Widget\"})\n\n;; Low-level: listen for a custom event\n(on-event container \"cart:add\" (fn (e)\n  (swap! items (fn (old) (append old (event-detail e))))))\n\n;; High-level: bridge an event directly to a signal\n;; Creates an effect with automatic cleanup on dispose\n(bridge-event container \"cart:add\" items\n  (fn (detail) (append (deref items) detail)))"
          "lisp"))
      (p "Platform interface required:")
      (div
        (~tw :tokens "overflow-x-auto rounded border border-stone-200 mt-2")
        (table
          (~tw :tokens "w-full text-left text-sm")
          (thead
            (tr
              (~tw :tokens "border-b border-stone-200 bg-stone-100")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Function")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Description")))
          (tbody
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700")
                "(dom-listen el name handler)")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Attach event listener, return remove function"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700")
                "(dom-dispatch el name detail)")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Dispatch CustomEvent with detail, bubbles: true"))
            (tr
              (td
                (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700")
                "(event-detail e)")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Extract .detail from CustomEvent"))))))))
