;; restart-demo.sx — Classic CL condition system demo
;;
;; Demonstrates resumable exceptions via restarts.
;; The `safe-divide` function signals a division-by-zero condition
;; and offers two restarts:
;;   :use-zero  — return 0 as the result
;;   :retry     — call safe-divide again with a corrected divisor
;;
;; Depends on: lib/common-lisp/runtime.sx already loaded.

;; ── safe-divide ────────────────────────────────────────────────────────────
;;
;; Divides numerator by denominator.
;; When denominator is 0, signals division-by-zero with two restarts.

(define
  safe-divide
  (fn
    (n d)
    (if
      (= d 0)
      (cl-restart-case
        (fn
          ()
          (cl-signal
            (cl-make-condition
              "division-by-zero"
              "operation"
              "/"
              "operands"
              (list n d)))
          (error "division by zero — no restart invoked"))
        (list "use-zero" (list) (fn () 0))
        (list "retry" (list "d") (fn (d2) (safe-divide n d2))))
      (/ n d))))

;; ── tests ─────────────────────────────────────────────────────────────────

(define passed 0)
(define failed 0)
(define failures (list))

(define
  check
  (fn
    (label got expected)
    (if
      (= got expected)
      (set! passed (+ passed 1))
      (begin
        (set! failed (+ failed 1))
        (set!
          failures
          (append
            failures
            (list
              (str
                "FAIL ["
                label
                "]: got="
                (inspect got)
                " expected="
                (inspect expected)))))))))

(define
  reset-stacks!
  (fn () (set! cl-handler-stack (list)) (set! cl-restart-stack (list))))

;; Normal division
(reset-stacks!)
(check "10 / 2 = 5" (safe-divide 10 2) 5)

;; Invoke use-zero restart
(reset-stacks!)
(check
  "10 / 0 -> use-zero"
  (cl-handler-bind
    (list
      (list "division-by-zero" (fn (c) (cl-invoke-restart "use-zero"))))
    (fn () (safe-divide 10 0)))
  0)

;; Invoke retry restart with a corrected denominator
(reset-stacks!)
(check
  "10 / 0 -> retry with 2"
  (cl-handler-bind
    (list
      (list
        "division-by-zero"
        (fn (c) (cl-invoke-restart "retry" 2))))
    (fn () (safe-divide 10 0)))
  5)

;; Nested calls: outer handles the inner divide-by-zero
(reset-stacks!)
(check
  "nested: 20 / (0->4) = 5"
  (cl-handler-bind
    (list
      (list
        "division-by-zero"
        (fn (c) (cl-invoke-restart "retry" 4))))
    (fn () (let ((r1 (safe-divide 20 0))) r1)))
  5)

;; handler-case — unwinding version
(reset-stacks!)
(check
  "handler-case: catches division-by-zero"
  (cl-handler-case
    (fn () (safe-divide 9 0))
    (list "division-by-zero" (fn (c) "caught!")))
  "caught!")

;; Verify use-zero is idempotent (two uses)
(reset-stacks!)
(check
  "two use-zero invocations"
  (cl-handler-bind
    (list
      (list "division-by-zero" (fn (c) (cl-invoke-restart "use-zero"))))
    (fn
      ()
      (+
        (safe-divide 10 0)
        (safe-divide 3 0))))
  0)

;; No restart needed for normal division
(reset-stacks!)
(check
  "no restart needed for 8/4"
  (safe-divide 8 4)
  2)

;; ── summary ────────────────────────────────────────────────────────────────

(define demo-passed passed)
(define demo-failed failed)
(define demo-failures failures)