;; lib/dream/run.sx — Dream-on-SX entry point.
;; dream-run installs a root handler into the existing SX HTTP server via
;; (perform (:http/listen …)) — it does NOT implement its own socket loop. The
;; host invokes the installed app per request with a raw request dict; the app
;; adapts it to a dream-request, runs the handler, and serialises the response
;; (status/headers/body/set-cookies, or a websocket upgrade). Depends on types.sx
;; + websocket.sx. The listen transport is injectable for testing.

;; ── response serialisation for the host ────────────────────────────
(define
  dr/serialize-response
  (fn (resp) (if (dream-websocket? resp) {:websocket (dream-ws-handler resp) :body "" :headers (dream-headers resp) :status 101 :set-cookies (list)} {:body (dream-resp-body resp) :headers (dream-headers resp) :status (dream-status resp) :set-cookies (dream-resp-cookies resp)})))

;; ── the app: raw host request -> serialised response ───────────────
(define
  dream-app
  (fn
    (handler)
    (fn
      (raw)
      (let
        ((req (dream-request (or (get raw :method) "GET") (or (get raw :target) (or (get raw :path) "/")) (or (get raw :headers) {}) (or (get raw :body) ""))))
        (dr/serialize-response (dream-coerce-response (handler req)))))))

;; ── dream-run ──────────────────────────────────────────────────────
(define dream-default-port 8080)

(define dream-run-with (fn (listen handler opts) (listen {:op "http/listen" :port (or (get opts :port) dream-default-port) :app (dream-app handler) :host (or (get opts :host) "0.0.0.0")})))

(define dream-perform-listen (fn (op) (perform op)))

(define
  dream-run
  (fn (handler) (dream-run-with dream-perform-listen handler {})))
(define
  dream-run-port
  (fn
    (handler port)
    (dream-run-with dream-perform-listen handler {:port port})))
(define
  dream-run-opts
  (fn (handler opts) (dream-run-with dream-perform-listen handler opts)))
