;; ---------------------------------------------------------------------------
;; Live Streaming — SSE & WebSocket
;; ---------------------------------------------------------------------------

(defcomp ()
  (~docs/page :title "Live Streaming"

    (~docs/section :title "Context" :id "context"
      (p "SX streaming currently uses chunked transfer encoding: the server sends an HTML shell with "
         (code "~shared:pages/suspense") " placeholders, then resolves each one via inline "
         (code "<script>__sxResolve(id, sx)</script>") " chunks as IO completes. "
         "Once the response finishes, the connection closes. Each slot resolves exactly once.")
      (p "This is powerful for initial page load but doesn't support live updates "
         "— dashboard metrics, chat messages, collaborative editing, real-time notifications. "
         "For that we need a persistent transport: " (strong "SSE") " (Server-Sent Events) or " (strong "WebSockets") ".")
      (p "The key insight: the client already has " (code "Sx.resolveSuspense(id, sxSource)") " which replaces "
         "DOM content by suspense ID. A persistent connection just needs to keep calling it."))

    (~docs/section :title "Design" :id "design"

      (~docs/subsection :title "Transport Hierarchy"
        (p "Three tiers, progressively more capable:")
        (ol (~tw :tokens "list-decimal list-inside space-y-2 text-stone-700 text-sm")
          (li (strong "Chunked streaming") " (done) — single HTTP response, each suspense resolves once. "
              "Best for: initial page load with slow IO.")
          (li (strong "SSE") " — persistent one-way connection, server pushes resolve events. "
              "Best for: dashboards, notifications, progress bars, any read-only live data.")
          (li (strong "WebSocket") " — bidirectional, client can send events back. "
              "Best for: chat, collaborative editing, interactive applications.")))

      (~docs/subsection :title "SSE Protocol"
        (p "A " (code "~live") " component declares a persistent connection to an SSE endpoint:")
        (~docs/code :src (highlight "(~live :src \"/api/stream/dashboard\"\n  (~shared:pages/suspense :id \"cpu\" :fallback (span \"Loading...\"))\n  (~shared:pages/suspense :id \"memory\" :fallback (span \"Loading...\"))\n  (~shared:pages/suspense :id \"requests\" :fallback (span \"Loading...\")))" "lisp"))
        (p "The server SSE endpoint yields SX resolve events:")
        (~docs/code :src (highlight "async def dashboard_stream():\n    while True:\n        stats = await get_system_stats()\n        yield sx_sse_event(\"cpu\", f'(~stat-badge :value \"{stats.cpu}%\")')\n        yield sx_sse_event(\"memory\", f'(~stat-badge :value \"{stats.mem}%\")')\n        await asyncio.sleep(1)" "python"))
        (p "SSE wire format — each event is a suspense resolve:")
        (~docs/code :src (highlight "event: sx-resolve\ndata: {\"id\": \"cpu\", \"sx\": \"(~stat-badge :value \\\"42%\\\")\"}\n\nevent: sx-resolve\ndata: {\"id\": \"memory\", \"sx\": \"(~stat-badge :value \\\"68%\\\")\"}" "text")))

      (~docs/subsection :title "WebSocket Protocol"
        (p "A " (code "~ws") " component establishes a bidirectional channel:")
        (~docs/code :src (highlight "(~ws :src \"/ws/chat\"\n  :on-message handle-chat-message\n  (~shared:pages/suspense :id \"messages\" :fallback (div \"Connecting...\"))\n  (~shared:pages/suspense :id \"typing\" :fallback (span)))" "lisp"))
        (p "Client can send SX expressions back:")
        (~docs/code :src (highlight ";; Client sends:\n(sx-send ws-conn '(chat-message :text \"hello\" :user \"alice\"))\n\n;; Server receives, broadcasts to all connected clients:\n;; event: sx-resolve for \"messages\" suspense" "lisp")))

      (~docs/subsection :title "Shared Resolution Mechanism"
        (p "All three transports use the same client-side resolution:")
        (ul (~tw :tokens "list-disc list-inside space-y-1 text-stone-600 text-sm")
          (li (code "Sx.resolveSuspense(id, sxSource)") " — already exists, parses SX and renders to DOM")
          (li "SSE: " (code "EventSource") " → " (code "onmessage") " → " (code "resolveSuspense()"))
          (li "WS: " (code "WebSocket") " → " (code "onmessage") " → " (code "resolveSuspense()"))
          (li "The component env (defs needed for rendering) can be sent once on connection open")
          (li "Subsequent events only need the SX expression — lightweight wire format"))))

    (~docs/section :title "Implementation" :id "implementation"

      (~docs/subsection :title "Phase 1: SSE Infrastructure"
        (ol (~tw :tokens "list-decimal list-inside space-y-2 text-stone-700 text-sm")
          (li "Add " (code "~live") " component to " (code "shared/sx/templates/") " — renders child suspense placeholders, "
              "emits " (code "data-sx-live") " attribute with SSE endpoint URL")
          (li "Add " (code "sx-live.js") " client module — on boot, finds " (code "[data-sx-live]") " elements, "
              "opens EventSource, routes events to " (code "resolveSuspense()"))
          (li "Add " (code "sx_sse_event(id, sx)") " helper for Python SSE endpoints — formats SSE wire protocol")
          (li "Add " (code "sse_stream()") " Quart helper — returns async generator Response with correct headers")))

      (~docs/subsection :title "Phase 2: Defpage Integration"
        (ol (~tw :tokens "list-decimal list-inside space-y-2 text-stone-700 text-sm")
          (li "New " (code ":live") " defpage slot — declares SSE endpoint + suspense bindings")
          (li "Auto-mount SSE endpoint alongside the page route")
          (li "Component defs sent as first SSE event on connection open")
          (li "Automatic reconnection with exponential backoff")))

      (~docs/subsection :title "Phase 3: WebSocket"
        (ol (~tw :tokens "list-decimal list-inside space-y-2 text-stone-700 text-sm")
          (li "Add " (code "~ws") " component — bidirectional channel with send/receive")
          (li "Add " (code "sx-ws.js") " client module — WebSocket management, message routing")
          (li "Server-side: Quart WebSocket handlers that receive and broadcast SX events")
          (li "Client-side: " (code "sx-send") " primitive for sending SX expressions to server")))

      (~docs/subsection :title "Phase 4: Spec & Boundary"
        (ol (~tw :tokens "list-decimal list-inside space-y-2 text-stone-700 text-sm")
          (li "Spec " (code "~live") " and " (code "~ws") " in " (code "render.sx") " (how they render in each mode)")
          (li "Add SSE/WS IO primitives to " (code "boundary.sx"))
          (li "Bootstrap SSE/WS connection management into " (code "sx-ref.js"))
          (li "Spec-level tests for resolve, reconnection, and message routing"))))

    (~docs/section :title "Files" :id "files"
      (table (~tw :tokens "w-full text-left border-collapse")
        (thead
          (tr (~tw :tokens "border-b border-stone-200")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "File")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Purpose")))
        (tbody
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/templates/live.sx")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "~live component definition"))
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/static/scripts/sx-live.js")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "SSE client — EventSource → resolveSuspense"))
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/sse.py")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "SSE helpers — event formatting, stream response"))
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/static/scripts/sx-ws.js")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "WebSocket client — bidirectional SX channel"))
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/render.sx")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "Spec: ~live and ~ws rendering in all modes"))
          (tr (~tw :tokens "border-b border-stone-100")
            (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/boundary.sx")
            (td (~tw :tokens "px-3 py-2 text-stone-700") "SSE/WS IO primitive declarations")))))))
