;; errors.sx — Phase 9 error / undefined / partial-fn coverage via hk-test-error.

;; ── error builtin ────────────────────────────────────────────
(define
  hk-as-list
  (fn
    (xs)
    (cond
      ((and (list? xs) (= (first xs) "[]")) (list))
      ((and (list? xs) (= (first xs) ":"))
        (cons (nth xs 1) (hk-as-list (nth xs 2))))
      (:else xs))))

(hk-test-error
  "error: raises with literal message"
  (fn () (hk-deep-force (hk-run "main = error \"boom\"")))
  "hk-error: boom")

(hk-test-error
  "error: raises with computed message"
  (fn () (hk-deep-force (hk-run "main = error (\"oops: \" ++ show 42)")))
  "hk-error: oops: 42")

;; ── undefined ────────────────────────────────────────────────
(hk-test-error
  "error: nested in if branch (only fires when forced)"
  (fn
    ()
    (hk-deep-force (hk-run "main = if 1 == 1 then error \"taken\" else 0")))
  "taken")

(hk-test-error
  "undefined: raises Prelude.undefined"
  (fn () (hk-deep-force (hk-run "main = undefined")))
  "Prelude.undefined")

;; The non-strict path: undefined doesn't fire when not forced.
(hk-test-error
  "undefined: forced via arithmetic"
  (fn () (hk-deep-force (hk-run "main = undefined + 1")))
  "Prelude.undefined")

;; ── partial functions ───────────────────────────────────────
(hk-test
  "undefined: lazy, not forced when discarded"
  (hk-deep-force (hk-run "main = let _ = undefined in 5"))
  5)

(hk-test-error
  "head []: raises Prelude.head: empty list"
  (fn () (hk-deep-force (hk-run "main = head []")))
  "Prelude.head: empty list")

(hk-test-error
  "tail []: raises Prelude.tail: empty list"
  (fn () (hk-deep-force (hk-run "main = tail []")))
  "Prelude.tail: empty list")

;; head and tail still work on non-empty lists.
(hk-test-error
  "fromJust Nothing: raises Maybe.fromJust: Nothing"
  (fn () (hk-deep-force (hk-run "main = fromJust Nothing")))
  "Maybe.fromJust: Nothing")

(hk-test
  "head [42]: still works"
  (hk-deep-force (hk-run "main = head [42]"))
  42)

;; ── error in IO context ─────────────────────────────────────
(hk-test
  "tail [1,2,3]: still works"
  (hk-as-list (hk-deep-force (hk-run "main = tail [1,2,3]")))
  (list 2 3))

(hk-test
  "hk-run-io: error in main lands in io-lines"
  (let
    ((lines (hk-run-io "main = error \"caught here\"")))
    (>= (index-of (str lines) "caught here") 0))
  true)

;; ── hk-test-error helper itself ─────────────────────────────
(hk-test
  "hk-run-io: putStrLn before error preserves earlier output"
  (let
    ((lines (hk-run-io "main = do { putStrLn \"first\"; error \"died\"; putStrLn \"never\" }")))
    (and
      (>= (index-of (str lines) "first") 0)
      (>= (index-of (str lines) "died") 0)))
  true)

;; hk-as-list helper for converting a forced Haskell cons into an SX list.
(hk-test-error
  "hk-test-error: matches partial substring inside wrapped exception"
  (fn () (hk-deep-force (hk-run "main = error \"unique-marker-xyz\"")))
  "unique-marker-xyz")

{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}
