(defsuite
  "handler-bind-basic"
  (deftest
    "handler-bind returns body value when no signal"
    (assert-equal
      42
      (handler-bind (((fn (c) true) (fn (c) "handled"))) 42)))
  (deftest
    "signal with matching handler returns handler value"
    (assert-equal
      "handled: boom"
      (handler-bind
        (((fn (c) true) (fn (c) (str "handled: " c))))
        (signal-condition "boom"))))
  (deftest
    "signal without handler errors"
    (assert-throws (fn () (signal-condition "unhandled"))))
  (deftest
    "signal returns handler value to call site"
    (assert-equal
      51
      (handler-bind
        (((fn (c) true) (fn (c) (* c 10))))
        (+ 1 (signal-condition 5)))))
  (deftest
    "handler-bind multiple handlers picks first match"
    (assert-equal
      "got-b"
      (handler-bind
        (((fn (c) (= c "type-a")) (fn (c) "got-a"))
          ((fn (c) (= c "type-b")) (fn (c) "got-b")))
        (signal-condition "type-b"))))
  (deftest
    "nested handler-bind inner takes precedence"
    (assert-equal
      "inner-handler"
      (handler-bind
        (((fn (c) true) (fn (c) "outer")))
        (handler-bind
          (((fn (c) (= c "inner")) (fn (c) "inner-handler")))
          (signal-condition "inner")))))
  (deftest
    "handler-bind with multi-expression body"
    (assert-equal
      10
      (handler-bind (((fn (c) true) (fn (c) c))) (+ 1 2) (* 2 5)))))

(defsuite
  "restart-case-basic"
  (deftest
    "restart-case returns body value when no signal"
    (assert-equal 3 (restart-case (+ 1 2) (use-default () 0))))
  (deftest
    "invoke-restart finds and runs restart"
    (assert-equal
      42
      (handler-bind
        (((fn (c) true) (fn (c) (invoke-restart (quote use-default)))))
        (restart-case
          (do (signal-condition "bad") "unreachable")
          (use-default () 42)))))
  (deftest
    "invoke-restart with argument"
    (assert-equal
      99
      (handler-bind
        (((fn (c) true) (fn (c) (invoke-restart (quote use-value) 99))))
        (restart-case
          (do (signal-condition "bad") "unreachable")
          (use-value (v) v)))))
  (deftest
    "invoke-restart unknown name errors"
    (assert-throws (fn () (invoke-restart (quote nonexistent)))))
  (deftest
    "nested restart-case uses inner restart"
    (assert-equal
      "inner-fallback"
      (handler-bind
        (((fn (c) true) (fn (c) (invoke-restart (quote fallback)))))
        (restart-case
          (restart-case
            (do (signal-condition "err") "unreachable")
            (fallback () "inner-fallback"))
          (fallback () "outer-fallback")))))
  (deftest
    "signal + restart computes replacement value"
    (assert-equal
      25
      (handler-bind
        (((fn (c) true) (fn (c) (invoke-restart (quote use-value) (* c 3)))))
        (+ 10 (restart-case (signal-condition 5) (use-value (v) v)))))))

(defsuite
  "comp-trace"
  (deftest
    "component call creates trace frame"
    (defcomp ~trace-inner () "inner")
    (assert-equal "inner" (~trace-inner)))
  (deftest
    "nested component calls work"
    (defcomp ~trace-child () "child")
    (defcomp ~trace-parent () (~trace-child))
    (assert-equal "child" (~trace-parent)))
  (deftest
    "component error via cek-try"
    (defcomp ~broken-comp () (error "deliberate"))
    (let
      ((result (cek-try (fn () (~broken-comp)))))
      (assert-true (= (type-of result) "list")))))
