(defsuite
  "stdlib-equality"
  (deftest
    "eq? delegates to ="
    (assert-true (eq? 1 1))
    (assert-false (eq? 1 2))
    (assert-true (eq? "a" "a")))
  (deftest
    "eqv? delegates to ="
    (assert-true (eqv? 42 42))
    (assert-false (eqv? 42 43)))
  (deftest
    "equal? delegates to ="
    (assert-true (equal? "hello" "hello"))
    (assert-false (equal? "hello" "world")))
  (deftest
    "equality on nil"
    (assert-true (eq? nil nil))
    (assert-true (eqv? nil nil))
    (assert-true (equal? nil nil)))
  (deftest
    "equality across types"
    (assert-false (eq? 1 "1"))
    (assert-false (eq? true 1))))

(defsuite
  "stdlib-type-predicates"
  (deftest
    "boolean?"
    (assert-true (boolean? true))
    (assert-true (boolean? false))
    (assert-false (boolean? 1))
    (assert-false (boolean? "true")))
  (deftest
    "number?"
    (assert-true (number? 42))
    (assert-true (number? 3.14))
    (assert-true (number? 0))
    (assert-false (number? "42")))
  (deftest
    "string?"
    (assert-true (string? "hello"))
    (assert-true (string? ""))
    (assert-false (string? 42)))
  (deftest
    "list?"
    (assert-true (list? (list 1 2 3)))
    (assert-true (list? (list)))
    (assert-false (list? "not a list")))
  (deftest
    "dict?"
    (assert-true (dict? {:a 1}))
    (assert-false (dict? (list 1 2))))
  (deftest
    "continuation? on non-continuation"
    (assert-false (continuation? 42))
    (assert-false (continuation? "hello"))))

(defsuite
  "stdlib-numeric-predicates"
  (deftest
    "zero?"
    (assert-true (zero? 0))
    (assert-false (zero? 1))
    (assert-false (zero? -1)))
  (deftest
    "odd?"
    (assert-true (odd? 1))
    (assert-true (odd? 3))
    (assert-true (odd? -1))
    (assert-false (odd? 0))
    (assert-false (odd? 2)))
  (deftest
    "even?"
    (assert-true (even? 0))
    (assert-true (even? 2))
    (assert-true (even? -2))
    (assert-false (even? 1))
    (assert-false (even? 3)))
  (deftest
    "empty? on list"
    (assert-true (empty? (list)))
    (assert-false (empty? (list 1))))
  (deftest
    "empty? on string"
    (assert-true (empty? ""))
    (assert-false (empty? "a")))
  (deftest "empty? on nil" (assert-true (empty? nil)))
  (deftest
    "empty? on dict"
    (assert-true (empty? {}))
    (assert-false (empty? {:a 1}))))

(defsuite
  "stdlib-numeric-ops"
  (deftest "abs positive" (assert-equal 5 (abs 5)))
  (deftest "abs negative" (assert-equal 5 (abs -5)))
  (deftest "abs zero" (assert-equal 0 (abs 0)))
  (deftest "ceil integer" (assert-equal 3 (ceil 3)))
  (deftest
    "ceil fractional"
    (assert-equal 4 (ceil 3.1))
    (assert-equal 4 (ceil 3.9)))
  (deftest "ceil negative" (assert-equal -3 (ceil -3.9)))
  (deftest "round integer" (assert-equal 3 (round 3)))
  (deftest
    "round fractional"
    (assert-equal 4 (round 3.5))
    (assert-equal 3 (round 3.4)))
  (deftest
    "round with ndigits"
    (assert-equal 3.1 (round 3.14 1))
    (assert-equal 3.14 (round 3.145 2)))
  (deftest "min" (assert-equal 1 (min 1 2)) (assert-equal -1 (min -1 0)))
  (deftest "max" (assert-equal 2 (max 1 2)) (assert-equal 0 (max -1 0)))
  (deftest "clamp within range" (assert-equal 5 (clamp 5 0 10)))
  (deftest "clamp below" (assert-equal 0 (clamp -5 0 10)))
  (deftest "clamp above" (assert-equal 10 (clamp 15 0 10))))

(defsuite
  "stdlib-list-ops"
  (deftest "first" (assert-equal 1 (first (list 1 2 3))))
  (deftest "first empty" (assert-nil (first (list))))
  (deftest "first nil" (assert-nil (first nil)))
  (deftest "last" (assert-equal 3 (last (list 1 2 3))))
  (deftest "last single" (assert-equal 1 (last (list 1))))
  (deftest "last empty" (assert-nil (last (list))))
  (deftest "rest" (assert-equal (list 2 3) (rest (list 1 2 3))))
  (deftest "rest single" (assert-equal (list) (rest (list 1))))
  (deftest "rest nil" (assert-equal (list) (rest nil)))
  (deftest "nth valid" (assert-equal 2 (nth (list 1 2 3) 1)))
  (deftest "nth first" (assert-equal 1 (nth (list 1 2 3) 0)))
  (deftest "nth out of bounds" (assert-nil (nth (list 1 2 3) 5)))
  (deftest "nth negative" (assert-nil (nth (list 1 2 3) -1)))
  (deftest "nth nil" (assert-nil (nth nil 0)))
  (deftest "cons" (assert-equal (list 0 1 2) (cons 0 (list 1 2))))
  (deftest "cons to nil" (assert-equal (list 0) (cons 0 nil)))
  (deftest
    "append element"
    (assert-equal (list 1 2 3) (append (list 1 2) 3)))
  (deftest
    "append list"
    (assert-equal (list 1 2 3 4) (append (list 1 2) (list 3 4))))
  (deftest "reverse" (assert-equal (list 3 2 1) (reverse (list 1 2 3))))
  (deftest "reverse empty" (assert-equal (list) (reverse (list))))
  (deftest
    "flatten"
    (assert-equal (list 1 2 3 4) (flatten (list (list 1 2) (list 3 4)))))
  (deftest
    "flatten mixed"
    (assert-equal (list 1 2 3) (flatten (list 1 (list 2 3)))))
  (deftest "range basic" (assert-equal (list 0 1 2) (range 0 3 1)))
  (deftest "range with step" (assert-equal (list 0 2 4) (range 0 6 2)))
  (deftest
    "chunk-every"
    (assert-equal
      (list (list 1 2) (list 3 4) (list 5))
      (chunk-every (list 1 2 3 4 5) 2)))
  (deftest
    "chunk-every even"
    (assert-equal
      (list (list 1 2 3) (list 4 5 6))
      (chunk-every (list 1 2 3 4 5 6) 3)))
  (deftest
    "zip-pairs"
    (assert-equal
      (list (list 1 2) (list 2 3) (list 3 4))
      (zip-pairs (list 1 2 3 4))))
  (deftest
    "zip-pairs short"
    (assert-equal (list (list 1 2)) (zip-pairs (list 1 2)))))

(defsuite
  "stdlib-dict-ops"
  (deftest
    "vals"
    (let
      ((v (vals {:b 2 :a 1})))
      (assert-equal 2 (len v))
      (assert-true (some (fn (x) (= x 1)) v))
      (assert-true (some (fn (x) (= x 2)) v))))
  (deftest "has-key? present" (assert-true (has-key? {:b 2 :a 1} "a")))
  (deftest "has-key? absent" (assert-false (has-key? {:a 1} "z")))
  (deftest
    "assoc"
    (let
      ((d (assoc {:a 1} "b" 2)))
      (assert-equal 1 (get d "a"))
      (assert-equal 2 (get d "b"))))
  (deftest
    "assoc overwrites"
    (let ((d (assoc {:a 1} "a" 99))) (assert-equal 99 (get d "a"))))
  (deftest
    "dissoc"
    (let
      ((d (dissoc {:b 2 :a 1} "a")))
      (assert-false (has-key? d "a"))
      (assert-true (has-key? d "b"))))
  (deftest
    "into list from list"
    (assert-equal (list 1 2 3 4) (into (list 1 2) (list 3 4))))
  (deftest
    "into dict from pairs"
    (let
      ((d (into {} (list (list "a" 1) (list "b" 2)))))
      (assert-equal 1 (get d "a"))
      (assert-equal 2 (get d "b")))))

(defsuite
  "stdlib-string-ops"
  (deftest "upcase" (assert-equal "HELLO" (upcase "hello")))
  (deftest "downcase" (assert-equal "hello" (downcase "HELLO")))
  (deftest
    "string-length"
    (assert-equal 5 (string-length "hello"))
    (assert-equal 0 (string-length "")))
  (deftest "substring" (assert-equal "ell" (substring "hello" 1 4)))
  (deftest
    "substring from start"
    (assert-equal "he" (substring "hello" 0 2)))
  (deftest
    "string-contains? found"
    (assert-true (string-contains? "hello world" "world")))
  (deftest
    "string-contains? not found"
    (assert-false (string-contains? "hello" "xyz")))
  (deftest
    "starts-with?"
    (assert-true (starts-with? "hello" "hel"))
    (assert-false (starts-with? "hello" "xyz")))
  (deftest
    "ends-with?"
    (assert-true (ends-with? "hello" "llo"))
    (assert-false (ends-with? "hello" "xyz")))
  (deftest
    "ends-with? suffix longer than string"
    (assert-false (ends-with? "hi" "hello"))))

(defsuite
  "stdlib-utility"
  (deftest
    "contains? string"
    (assert-true (contains? "hello world" "world"))
    (assert-false (contains? "hello" "xyz")))
  (deftest
    "contains? list"
    (assert-true (contains? (list 1 2 3) 2))
    (assert-false (contains? (list 1 2 3) 4)))
  (deftest
    "contains? dict"
    (assert-true (contains? {:a 1} "a"))
    (assert-false (contains? {:a 1} "b")))
  (deftest "contains? other returns false" (assert-false (contains? 42 1)))
  (deftest
    "pluralize singular"
    (assert-equal "item" (pluralize 1 "item" "items")))
  (deftest
    "pluralize plural"
    (assert-equal "items" (pluralize 2 "item" "items")))
  (deftest
    "pluralize zero"
    (assert-equal "items" (pluralize 0 "item" "items")))
  (deftest
    "escape html entities"
    (assert-equal "&amp;&lt;&gt;&quot;&#x27;" (escape "&<>\"'")))
  (deftest
    "escape plain string unchanged"
    (assert-equal "hello" (escape "hello")))
  (deftest "assert passes" (assert-equal true (assert true "should pass")))
  (deftest
    "parse-datetime passthrough"
    (assert-equal "2024-01-01" (parse-datetime "2024-01-01")))
  (deftest "parse-datetime nil" (assert-nil (parse-datetime nil))))
