;; Pattern-matcher tests. The matcher takes (pat val env) and returns
;; an extended env dict on success, or `nil` on failure. Constructor
;; values are tagged lists (con-name first); tuples use the "Tuple"
;; tag; lists use chained `:` cons with `[]` nil.

;; ── Atomic patterns ──
(hk-test
  "wildcard always matches"
  (hk-match (list :p-wild) 42 (dict))
  (dict))

(hk-test
  "var binds value"
  (hk-match (list :p-var "x") 42 (dict))
  {:x 42})

(hk-test
  "var preserves prior env"
  (hk-match (list :p-var "y") 7 {:x 1})
  {:x 1 :y 7})

(hk-test
  "int literal matches equal"
  (hk-match (list :p-int 5) 5 (dict))
  (dict))

(hk-test
  "int literal fails on mismatch"
  (hk-match (list :p-int 5) 6 (dict))
  nil)

(hk-test
  "negative int literal matches"
  (hk-match (list :p-int -3) -3 (dict))
  (dict))

(hk-test
  "string literal matches"
  (hk-match (list :p-string "hi") "hi" (dict))
  (dict))

(hk-test
  "string literal fails"
  (hk-match (list :p-string "hi") "bye" (dict))
  nil)

(hk-test
  "char literal matches"
  (hk-match (list :p-char "a") "a" (dict))
  (dict))

;; ── Constructor patterns ──
(hk-test
  "0-arity con matches"
  (hk-match
    (list :p-con "Nothing" (list))
    (hk-mk-con "Nothing" (list))
    (dict))
  (dict))

(hk-test
  "1-arity con matches and binds"
  (hk-match
    (list :p-con "Just" (list (list :p-var "y")))
    (hk-mk-con "Just" (list 9))
    (dict))
  {:y 9})

(hk-test
  "con name mismatch fails"
  (hk-match
    (list :p-con "Just" (list (list :p-var "y")))
    (hk-mk-con "Nothing" (list))
    (dict))
  nil)

(hk-test
  "con arity mismatch fails"
  (hk-match
    (list :p-con "Pair" (list (list :p-var "a") (list :p-var "b")))
    (hk-mk-con "Pair" (list 1))
    (dict))
  nil)

(hk-test
  "nested con: Just (Just x)"
  (hk-match
    (list
      :p-con
      "Just"
      (list
        (list
          :p-con
          "Just"
          (list (list :p-var "x")))))
    (hk-mk-con "Just" (list (hk-mk-con "Just" (list 42))))
    (dict))
  {:x 42})

;; ── Tuple patterns ──
(hk-test
  "2-tuple matches and binds"
  (hk-match
    (list
      :p-tuple
      (list (list :p-var "a") (list :p-var "b")))
    (hk-mk-tuple (list 10 20))
    (dict))
  {:a 10 :b 20})

(hk-test
  "tuple arity mismatch fails"
  (hk-match
    (list
      :p-tuple
      (list (list :p-var "a") (list :p-var "b")))
    (hk-mk-tuple (list 10 20 30))
    (dict))
  nil)

;; ── List patterns ──
(hk-test
  "[] pattern matches empty list"
  (hk-match (list :p-list (list)) (hk-mk-nil) (dict))
  (dict))

(hk-test
  "[] pattern fails on non-empty"
  (hk-match (list :p-list (list)) (hk-mk-list (list 1)) (dict))
  nil)

(hk-test
  "[a] pattern matches singleton"
  (hk-match
    (list :p-list (list (list :p-var "a")))
    (hk-mk-list (list 7))
    (dict))
  {:a 7})

(hk-test
  "[a, b] pattern matches pair-list and binds"
  (hk-match
    (list
      :p-list
      (list (list :p-var "a") (list :p-var "b")))
    (hk-mk-list (list 1 2))
    (dict))
  {:a 1 :b 2})

(hk-test
  "[a, b] fails on too-long list"
  (hk-match
    (list
      :p-list
      (list (list :p-var "a") (list :p-var "b")))
    (hk-mk-list (list 1 2 3))
    (dict))
  nil)

;; Cons-style infix pattern (which the parser produces as :p-con ":")
(hk-test
  "cons (h:t) on non-empty list"
  (hk-match
    (list
      :p-con
      ":"
      (list (list :p-var "h") (list :p-var "t")))
    (hk-mk-list (list 1 2 3))
    (dict))
  {:h 1 :t (list ":" 2 (list ":" 3 (list "[]")))})

(hk-test
  "cons fails on empty list"
  (hk-match
    (list
      :p-con
      ":"
      (list (list :p-var "h") (list :p-var "t")))
    (hk-mk-nil)
    (dict))
  nil)

;; ── as patterns ──
(hk-test
  "as binds whole + sub-pattern"
  (hk-match
    (list
      :p-as
      "all"
      (list :p-con "Just" (list (list :p-var "x"))))
    (hk-mk-con "Just" (list 99))
    (dict))
  {:all (list "Just" 99) :x 99})

(hk-test
  "as on wildcard binds whole"
  (hk-match
    (list :p-as "v" (list :p-wild))
    "anything"
    (dict))
  {:v "anything"})

(hk-test
  "as fails when sub-pattern fails"
  (hk-match
    (list
      :p-as
      "n"
      (list :p-con "Just" (list (list :p-var "x"))))
    (hk-mk-con "Nothing" (list))
    (dict))
  nil)

;; ── lazy ~ pattern (eager equivalent for now) ──
(hk-test
  "lazy pattern eager-matches its inner"
  (hk-match
    (list :p-lazy (list :p-var "y"))
    42
    (dict))
  {:y 42})

;; ── Source-driven: parse a real Haskell pattern, match a value ──
(hk-test
  "parsed pattern: Just x against Just 5"
  (hk-match
    (hk-parse-pat-source "Just x")
    (hk-mk-con "Just" (list 5))
    (dict))
  {:x 5})

(hk-test
  "parsed pattern: x : xs against [10, 20, 30]"
  (hk-match
    (hk-parse-pat-source "x : xs")
    (hk-mk-list (list 10 20 30))
    (dict))
  {:x 10 :xs (list ":" 20 (list ":" 30 (list "[]")))})

(hk-test
  "parsed pattern: (a, b) against (1, 2)"
  (hk-match
    (hk-parse-pat-source "(a, b)")
    (hk-mk-tuple (list 1 2))
    (dict))
  {:a 1 :b 2})

(hk-test
  "parsed pattern: n@(Just x) against Just 7"
  (hk-match
    (hk-parse-pat-source "n@(Just x)")
    (hk-mk-con "Just" (list 7))
    (dict))
  {:n (list "Just" 7) :x 7})

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