;; lib/dream/tests/types.sx — request/response/route records + convenience.

(define dream-ty-pass 0)
(define dream-ty-fail 0)
(define dream-ty-fails (list))

(define
  dream-ty-test
  (fn
    (name actual expected)
    (if
      (= actual expected)
      (set! dream-ty-pass (+ dream-ty-pass 1))
      (begin
        (set! dream-ty-fail (+ dream-ty-fail 1))
        (append! dream-ty-fails {:name name :actual actual :expected expected})))))

;; ── request construction + accessors ───────────────────────────────
(define
  dream-ty-req
  (dream-request "get" "/users/42?tab=info&x=1" {:X-Token "abc" :Content-Type "text/html"} "hello"))

(dream-ty-test "method uppercased" (dream-method dream-ty-req) "GET")
(dream-ty-test "path strips query" (dream-path dream-ty-req) "/users/42")
(dream-ty-test
  "target keeps query"
  (dream-target dream-ty-req)
  "/users/42?tab=info&x=1")
(dream-ty-test "body" (dream-body dream-ty-req) "hello")
(dream-ty-test
  "header case-insensitive"
  (dream-header dream-ty-req "content-type")
  "text/html")
(dream-ty-test
  "header mixed case"
  (dream-header dream-ty-req "X-Token")
  "abc")
(dream-ty-test
  "missing header is nil"
  (dream-header dream-ty-req "absent")
  nil)
(dream-ty-test
  "query param tab"
  (dream-query-param dream-ty-req "tab")
  "info")
(dream-ty-test "query param x" (dream-query-param dream-ty-req "x") "1")
(dream-ty-test "params empty by default" (dream-param dream-ty-req "id") nil)
(dream-ty-test "is a request" (dream-request? dream-ty-req) true)
(dream-ty-test "string is not a request" (dream-request? "x") false)

;; ── query edge cases ───────────────────────────────────────────────
(dream-ty-test
  "no query is empty"
  (dream-query-param (dream-request "GET" "/plain" {} "") "k")
  nil)
(dream-ty-test
  "valueless query param"
  (dream-query-param (dream-request "GET" "/p?flag" {} "") "flag")
  "")

;; ── path params ────────────────────────────────────────────────────
(define dream-ty-req2 (dream-with-param dream-ty-req "id" "42"))
(dream-ty-test "with-param sets" (dream-param dream-ty-req2 "id") "42")
(dream-ty-test "with-param immutable" (dream-param dream-ty-req "id") nil)
(define dream-ty-req3 (dream-with-params dream-ty-req {:a "1" :b "2"}))
(dream-ty-test "with-params a" (dream-param dream-ty-req3 "a") "1")
(dream-ty-test "with-params b" (dream-param dream-ty-req3 "b") "2")

;; ── request convenience ────────────────────────────────────────────
(dream-ty-test "queries dict" (dream-queries dream-ty-req) {:x "1" :tab "info"})
(dream-ty-test
  "query-or present"
  (dream-query-param-or dream-ty-req "tab" "def")
  "info")
(dream-ty-test
  "query-or default"
  (dream-query-param-or dream-ty-req "missing" "def")
  "def")
(dream-ty-test "has-query yes" (dream-has-query? dream-ty-req "tab") true)
(dream-ty-test "has-query no" (dream-has-query? dream-ty-req "nope") false)
(dream-ty-test
  "header-or present"
  (dream-header-or dream-ty-req "x-token" "d")
  "abc")
(dream-ty-test
  "header-or default"
  (dream-header-or dream-ty-req "x-absent" "d")
  "d")
(dream-ty-test
  "has-header yes"
  (dream-has-header? dream-ty-req "Content-Type")
  true)
(dream-ty-test
  "has-header no"
  (dream-has-header? dream-ty-req "x-absent")
  false)
(dream-ty-test "param-or default" (dream-param-or dream-ty-req "id" "0") "0")
(dream-ty-test
  "param-or present"
  (dream-param-or dream-ty-req2 "id" "0")
  "42")
(dream-ty-test
  "content-type-of"
  (dream-content-type-of dream-ty-req)
  "text/html")
(dream-ty-test "method-is yes" (dream-method-is? dream-ty-req "get") true)
(dream-ty-test "method-is no" (dream-method-is? dream-ty-req "post") false)
(define dream-ty-jreq (dream-request "GET" "/" {:Accept "application/json, text/html"} ""))
(dream-ty-test
  "accepts json"
  (dream-accepts? dream-ty-jreq "application/json")
  true)
(dream-ty-test
  "accepts missing"
  (dream-accepts? dream-ty-req "application/json")
  false)
(dream-ty-test "wants-json yes" (dream-wants-json? dream-ty-jreq) true)
(dream-ty-test "wants-json no" (dream-wants-json? dream-ty-req) false)

;; ── response construction ──────────────────────────────────────────
(dream-ty-test "html status" (dream-status (dream-html "<p>")) 200)
(dream-ty-test "html body" (dream-resp-body (dream-html "<p>")) "<p>")
(dream-ty-test
  "html content-type"
  (dream-resp-header (dream-html "<p>") "content-type")
  "text/html; charset=utf-8")
(dream-ty-test
  "text content-type"
  (dream-resp-header (dream-text "hi") "content-type")
  "text/plain; charset=utf-8")
(dream-ty-test
  "json content-type"
  (dream-resp-header (dream-json "{}") "content-type")
  "application/json")
(dream-ty-test
  "html-status code"
  (dream-status (dream-html-status 201 "ok"))
  201)
(dream-ty-test
  "not-found status"
  (dream-status (dream-not-found))
  404)
(dream-ty-test
  "empty status"
  (dream-status (dream-empty 204))
  204)
(dream-ty-test "empty body" (dream-resp-body (dream-empty 204)) "")
(dream-ty-test
  "redirect status"
  (dream-status (dream-redirect "/home"))
  303)
(dream-ty-test
  "redirect location"
  (dream-resp-header (dream-redirect "/home") "location")
  "/home")
(dream-ty-test
  "redirect-status code"
  (dream-status (dream-redirect-status 301 "/x"))
  301)
(dream-ty-test "is a response" (dream-response? (dream-html "x")) true)

;; ── response mutation ──────────────────────────────────────────────
(define dream-ty-resp (dream-add-header (dream-html "x") "X-Custom" "yes"))
(dream-ty-test
  "add-header"
  (dream-resp-header dream-ty-resp "x-custom")
  "yes")
(dream-ty-test "add-header keeps body" (dream-resp-body dream-ty-resp) "x")
(dream-ty-test
  "set-status"
  (dream-status (dream-set-status (dream-html "x") 500))
  500)

;; ── coercion ───────────────────────────────────────────────────────
(dream-ty-test
  "coerce string"
  (dream-status (dream-coerce-response "hi"))
  200)
(dream-ty-test
  "coerce string body"
  (dream-resp-body (dream-coerce-response "hi"))
  "hi")
(dream-ty-test
  "coerce response passthrough"
  (dream-status (dream-coerce-response (dream-empty 204)))
  204)

;; ── route ──────────────────────────────────────────────────────────
(define dream-ty-h (fn (req) (dream-text "ok")))
(define dream-ty-route (dream-route "post" "/submit" dream-ty-h))
(dream-ty-test "route method" (dream-route-method dream-ty-route) "POST")
(dream-ty-test "route path" (dream-route-path dream-ty-route) "/submit")
(dream-ty-test "route is route" (dream-route? dream-ty-route) true)
(dream-ty-test
  "route handler invokes"
  (dream-resp-body ((dream-route-handler dream-ty-route) dream-ty-req))
  "ok")

(define dream-ty-tests-run! (fn () {:total (+ dream-ty-pass dream-ty-fail) :passed dream-ty-pass :failed dream-ty-fail :fails dream-ty-fails}))
