# Systematic coverage tests for Clojure language features
(use ../../src/jolt/api)
(defn ct-eval [ctx s] (normalize-pvecs (eval-string ctx s)))

(print "Systematic Coverage Tests")

# --- Type Predicates ---
(print "1: type predicates...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(integer? 0)")) "integer? 0")
  (assert (= true (ct-eval ctx "(integer? 1)")) "integer? 1")
  (assert (= true (ct-eval ctx "(integer? -5)")) "integer? negative")
  (assert (= false (ct-eval ctx "(integer? 1.5)")) "integer? float")
  (assert (= false (ct-eval ctx "(integer? nil)")) "integer? nil")
  (assert (= false (ct-eval ctx "(integer? \"abc\")")) "integer? string")
  (assert (= true (ct-eval ctx "(boolean? true)")) "boolean? true")
  (assert (= true (ct-eval ctx "(boolean? false)")) "boolean? false")
  (assert (= false (ct-eval ctx "(boolean? 1)")) "boolean? falsy")
  (assert (= false (ct-eval ctx "(boolean? nil)")) "boolean? nil")
  (assert (= true (ct-eval ctx "(nil? nil)")) "nil? nil")
  (assert (= false (ct-eval ctx "(nil? false)")) "nil? false")
  (assert (= false (ct-eval ctx "(nil? 0)")) "nil? 0")
  (assert (= false (ct-eval ctx "(nil? [])")) "nil? empty vec")
  (assert (= false (ct-eval ctx "(some? nil)")) "some? nil")
  (assert (= true (ct-eval ctx "(some? false)")) "some? false")
  (assert (= true (ct-eval ctx "(some? 0)")) "some? 0")
  (assert (= true (ct-eval ctx "(some? [])")) "some? empty vec")
  (assert (= true (ct-eval ctx "(string? \"hello\")")) "string?")
  (assert (= false (ct-eval ctx "(string? 42)")) "string? false")
  (assert (= true (ct-eval ctx "(string? \"\")")) "string? empty")
  (assert (= true (ct-eval ctx "(number? 42)")) "number? int")
  (assert (= true (ct-eval ctx "(number? 3.14)")) "number? float")
  (assert (= false (ct-eval ctx "(number? \"42\")")) "number? string")
  (assert (= true (ct-eval ctx "(fn? inc)")) "fn? builtin")
  (assert (= false (ct-eval ctx "(fn? 42)")) "fn? false")
  (assert (= true (ct-eval ctx "(keyword? :foo)")) "keyword?")
  (assert (= false (ct-eval ctx "(keyword? \"foo\")")) "keyword? false")
  (assert (= true (ct-eval ctx "(symbol? 'abc)")) "symbol?")
  (assert (= false (ct-eval ctx "(symbol? :abc)")) "symbol? false")
  (assert (= true (ct-eval ctx "(map? {:a 1})")) "map?")
  (assert (= false (ct-eval ctx "(map? [1 2])")) "map? false")
  (assert (= true (ct-eval ctx "(set? #{1 2})")) "set?")
  (assert (= false (ct-eval ctx "(set? [1 2])")) "set? false")
  (assert (= true (ct-eval ctx "(seq? '(1 2))")) "seq? list")
  # vectors are not ISeq in Clojure — (seq? [1 2]) is false
  (assert (= false (ct-eval ctx "(seq? [1 2])")) "seq? vector is false")
  (assert (= true (ct-eval ctx "(coll? [1 2])")) "coll? vec")
  (assert (= true (ct-eval ctx "(coll? '(1 2))")) "coll? list")
  (assert (= true (ct-eval ctx "(coll? {})")) "coll? map")
  (assert (= true (ct-eval ctx "(true? true)")) "true? true")
  (assert (= false (ct-eval ctx "(true? 1)")) "true? 1")
  (assert (= true (ct-eval ctx "(false? false)")) "false? false")
  (assert (= false (ct-eval ctx "(false? nil)")) "false? nil")
  (assert (= true (ct-eval ctx "(identical? 42 42)")) "identical? same value"))
(print "  ok")

# --- Number Predicates ---
(print "2: number predicates...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(zero? 0)")) "zero? 0")
  (assert (= false (ct-eval ctx "(zero? 1)")) "zero? 1")
  # zero?/pos?/neg? now reject non-numbers (Clojure-strict), like the JVM.
  (assert (not ((protect (ct-eval ctx "(zero? nil)")) 0)) "zero? nil throws")
  (assert (= true (ct-eval ctx "(pos? 1)")) "pos?")
  (assert (= false (ct-eval ctx "(pos? 0)")) "pos? 0")
  (assert (= false (ct-eval ctx "(pos? -1)")) "pos? -1")
  (assert (= true (ct-eval ctx "(neg? -1)")) "neg?")
  (assert (= false (ct-eval ctx "(neg? 0)")) "neg? 0")
  (assert (= false (ct-eval ctx "(neg? 1)")) "neg? 1")
  (assert (= true (ct-eval ctx "(even? 0)")) "even? 0")
  (assert (= true (ct-eval ctx "(even? 2)")) "even? 2")
  (assert (= false (ct-eval ctx "(even? 1)")) "even? 1")
  (assert (= true (ct-eval ctx "(odd? 1)")) "odd? 1")
  (assert (= true (ct-eval ctx "(odd? 3)")) "odd? 3")
  (assert (= false (ct-eval ctx "(odd? 2)")) "odd? 2"))
(print "  ok")

# --- Math ---
(print "3: math operations...")
(let [ctx (init-cached)]
  (assert (= 0 (ct-eval ctx "(+)")) "+ 0 args")
  (assert (= 5 (ct-eval ctx "(+ 2 3)")) "+ 2 args")
  (assert (= 10 (ct-eval ctx "(+ 1 2 3 4)")) "+ varargs")
  (assert (= -5 (ct-eval ctx "(- 5)")) "- unary")
  (assert (= 2 (ct-eval ctx "(- 5 3)")) "- binary")
  (assert (= 1 (ct-eval ctx "(*)")) "* 0 args")
  (assert (= 6 (ct-eval ctx "(* 2 3)")) "*")
  (assert (= 0.5 (ct-eval ctx "(/ 2)")) "/ unary reciprocal")
  (assert (= 2 (ct-eval ctx "(/ 4 2)")) "/ binary")
  (assert (= 2 (ct-eval ctx "(quot 5 2)")) "quot")
  (assert (= 1 (ct-eval ctx "(rem 5 2)")) "rem")
  (assert (= 1 (ct-eval ctx "(mod 5 2)")) "mod")
  (assert (= 43 (ct-eval ctx "(inc 42)")) "inc")
  (assert (= 41 (ct-eval ctx "(dec 42)")) "dec")
  (assert (= 3 (ct-eval ctx "(max 1 2 3)")) "max")
  (assert (= 1 (ct-eval ctx "(min 1 2 3)")) "min")
  (assert (= 5 (ct-eval ctx "(abs -5)")) "abs negative")
  (assert (= 5 (ct-eval ctx "(abs 5)")) "abs positive")
  (assert (= 0 (ct-eval ctx "(abs 0)")) "abs 0")
  (assert (= 3.14 (ct-eval ctx "(abs -3.14)")) "abs float")
  (assert (= true (ct-eval ctx "(< (rand) 1)")) "rand < 1")
  (assert (= true (ct-eval ctx "(number? (rand-int 10))")) "rand-int type"))
(print "  ok")

# --- Comparison ---
(print "4: comparison...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(= 1 1)")) "= same")
  (assert (= true (ct-eval ctx "(= 1 1 1)")) "= multi same")
  (assert (= false (ct-eval ctx "(= 1 2)")) "= different")
  (assert (= true (ct-eval ctx "(not= 1 2)")) "not= different")
  (assert (= false (ct-eval ctx "(not= 1 1)")) "not= same")
  (assert (= true (ct-eval ctx "(< 1 2)")) "<")
  (assert (= false (ct-eval ctx "(< 2 1)")) "< false")
  (assert (= true (ct-eval ctx "(> 2 1)")) ">")
  (assert (= true (ct-eval ctx "(<= 1 1)")) "<=")
  (assert (= true (ct-eval ctx "(>= 2 2)")) ">="))
(print "  ok")

# --- Boolean logic ---
(print "5: boolean logic...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(and)")) "and empty")
  (assert (= true (ct-eval ctx "(and true)")) "and true")
  (assert (= nil (ct-eval ctx "(and nil)")) "and nil")
  (assert (= false (ct-eval ctx "(and false)")) "and false")
  (assert (= 3 (ct-eval ctx "(and 1 2 3)")) "and last")
  (assert (= nil (ct-eval ctx "(and 1 nil 3)")) "and short-circuit")
  (assert (= nil (ct-eval ctx "(or)")) "or empty")
  (assert (= true (ct-eval ctx "(or true)")) "or true")
  (assert (= nil (ct-eval ctx "(or nil)")) "or nil")
  (assert (= false (ct-eval ctx "(or false)")) "or false")
  (assert (= 1 (ct-eval ctx "(or nil false 1 2)")) "or first truthy")
  (assert (= false (ct-eval ctx "(or nil false)")) "or all falsy")
  (assert (= false (ct-eval ctx "(not true)")) "not true")
  (assert (= true (ct-eval ctx "(not nil)")) "not nil")
  (assert (= true (ct-eval ctx "(not false)")) "not false"))
(print "  ok")

# --- Collections ---
(print "6: collections...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(empty? nil)")) "empty? nil")
  (assert (= true (ct-eval ctx "(empty? [])")) "empty? []")
  (assert (= true (ct-eval ctx "(empty? ())")) "empty? ()")
  (assert (= true (ct-eval ctx "(empty? {})")) "empty? {}")
  (assert (= true (ct-eval ctx "(empty? #{})")) "empty? #{}")
  (assert (= true (ct-eval ctx "(empty? \"\")")) "empty? empty string")
  (assert (= false (ct-eval ctx "(empty? [1])")) "empty? non-empty")
  (assert (= true (ct-eval ctx "(every? even? [2 4 6])")) "every? true")
  (assert (= false (ct-eval ctx "(every? even? [2 3 4])")) "every? false")
  (assert (= 3 (ct-eval ctx "(count [1 2 3])")) "count vector")
  (assert (= 2 (ct-eval ctx "(count {:a 1 :b 2})")) "count map")
  (assert (= 3 (ct-eval ctx "(count #{1 2 3})")) "count set")
  (assert (= 3 (ct-eval ctx "(count \"abc\")")) "count string"))
(print "  ok")

# --- Sequence Operations ---
(print "7: sequence operations...")
(let [ctx (init-cached)]
  (assert (= 1 (ct-eval ctx "(first [1 2 3])")) "first")
  (assert (= nil (ct-eval ctx "(first [])")) "first empty")
  (assert (= nil (ct-eval ctx "(first nil)")) "first nil")
  (assert (= :a (ct-eval ctx "(nth [:a :b :c] 0)")) "nth 0")
  (assert (= :c (ct-eval ctx "(nth [:a :b :c] 2)")) "nth 2")
  (assert (= :d (ct-eval ctx "(nth [:a :b :c] 3 :d)")) "nth default")
  (assert (= nil (ct-eval ctx "(seq [])")) "seq empty -> nil")
  (assert (= nil (ct-eval ctx "(seq nil)")) "seq nil -> nil")
  (assert (= true (ct-eval ctx "(= [2 3 4] (map inc [1 2 3]))")) "map")
  (assert (= true (ct-eval ctx "(= [2 4] (filter even? [1 2 3 4]))")) "filter")
  (assert (= 6 (ct-eval ctx "(reduce + [1 2 3])")) "reduce")
  (assert (= 10 (ct-eval ctx "(reduce + 4 [1 2 3])")) "reduce init")
  (assert (= true (ct-eval ctx "(= [1 2] (take 2 [1 2 3 4]))")) "take")
  (assert (= true (ct-eval ctx "(= [3 4] (drop 2 [1 2 3 4]))")) "drop")
  (assert (= true (ct-eval ctx "(= [3 2 1] (reverse [1 2 3]))")) "reverse")
  (assert (= true (ct-eval ctx "(= 3 (count (distinct [1 1 2 2 3 3])))")) "distinct"))
(print "  ok")

# --- Collections: conj, assoc, dissoc, get ---
(print "8: collection mutation...")
(let [ctx (init-cached)]
  (assert (= true (ct-eval ctx "(= [1 2 3] (conj [1 2] 3))")) "conj vector")
  (assert (= true (ct-eval ctx "(= (quote (0 1 2)) (conj (quote (1 2)) 0))")) "conj list prepend")
  (assert (= true (ct-eval ctx "(= {:a 1 :b 2} (assoc {:a 1} :b 2))")) "assoc")
  (assert (= true (ct-eval ctx "(= {:a 1} (dissoc {:a 1 :b 2} :b))")) "dissoc")
  (assert (= 1 (ct-eval ctx "(get {:a 1} :a)")) "get")
  (assert (= nil (ct-eval ctx "(get {:a 1} :z)")) "get missing")
  (assert (= :d (ct-eval ctx "(get {:a 1} :z :d)")) "get default")
  (assert (= true (ct-eval ctx "(contains? {:a 1} :a)")) "contains? map")
  (assert (= false (ct-eval ctx "(contains? {:a 1} :z)")) "contains? missing")
  (assert (= true (ct-eval ctx "(contains? [5 6 7] 1)")) "contains? vector")
  (assert (= false (ct-eval ctx "(contains? [5 6 7] 3)")) "contains? vector oob"))
(print "  ok")

# --- Higher-order functions ---
(print "9: higher-order functions...")
(let [ctx (init-cached)]
  (assert (= 3 (ct-eval ctx "((comp inc inc) 1)")) "comp")
  (assert (= 42 (ct-eval ctx "(identity 42)")) "identity")
  (assert (= 99 (ct-eval ctx "((constantly 99) :anything)")) "constantly")
  (assert (= true (ct-eval ctx "(= 10 ((partial + 3 7)))")) "partial")
  (assert (= true (ct-eval ctx "((every-pred even? pos?) 4)")) "every-pred true")
  (assert (= false (ct-eval ctx "((every-pred even? pos?) -4)")) "every-pred false")
  (assert (= false (ct-eval ctx "((complement even?) 2)")) "complement"))
(print "  ok")

# --- Destructuring ---
(print "10: destructuring...")
(let [ctx (init-cached)]
  (assert (= 1 (ct-eval ctx "(let [[x] [1 2 3]] x)")) "seq destructure first")
  (assert (= 2 (ct-eval ctx "(let [[_ y] [1 2 3]] y)")) "seq destructure second"))
(print "  ok")

(print "\nAll Systematic Coverage tests passed!")
