;; ==========================================================================
;; primitives.sx — Specification of all SX built-in pure functions
;;
;; Each entry declares: name, parameter signature, and semantics.
;; Bootstrap compilers implement these natively per target.
;;
;; This file is a SPECIFICATION, not executable code. The define-primitive
;; form is a declarative macro that bootstrap compilers consume to generate
;; native primitive registrations.
;;
;; Format:
;;   (define-primitive "name"
;;     :params (param1 param2 &rest rest)
;;     :returns "type"
;;     :doc "description"
;;     :body (reference-implementation ...))
;;
;; The :body is optional — when provided, it gives a reference
;; implementation in SX that bootstrap compilers MAY use for testing
;; or as a fallback. Most targets will implement natively for performance.
;; ==========================================================================


;; --------------------------------------------------------------------------
;; Arithmetic
;; --------------------------------------------------------------------------

(define-primitive "+"
  :params (&rest args)
  :returns "number"
  :doc "Sum all arguments."
  :body (reduce (fn (a b) (native-add a b)) 0 args))

(define-primitive "-"
  :params (a &rest b)
  :returns "number"
  :doc "Subtract. Unary: negate. Binary: a - b."
  :body (if (empty? b) (native-neg a) (native-sub a (first b))))

(define-primitive "*"
  :params (&rest args)
  :returns "number"
  :doc "Multiply all arguments."
  :body (reduce (fn (a b) (native-mul a b)) 1 args))

(define-primitive "/"
  :params (a b)
  :returns "number"
  :doc "Divide a by b."
  :body (native-div a b))

(define-primitive "mod"
  :params (a b)
  :returns "number"
  :doc "Modulo a % b."
  :body (native-mod a b))

(define-primitive "sqrt"
  :params (x)
  :returns "number"
  :doc "Square root.")

(define-primitive "pow"
  :params (x n)
  :returns "number"
  :doc "x raised to power n.")

(define-primitive "abs"
  :params (x)
  :returns "number"
  :doc "Absolute value.")

(define-primitive "floor"
  :params (x)
  :returns "number"
  :doc "Floor to integer.")

(define-primitive "ceil"
  :params (x)
  :returns "number"
  :doc "Ceiling to integer.")

(define-primitive "round"
  :params (x &rest ndigits)
  :returns "number"
  :doc "Round to ndigits decimal places (default 0).")

(define-primitive "min"
  :params (&rest args)
  :returns "number"
  :doc "Minimum. Single list arg or variadic.")

(define-primitive "max"
  :params (&rest args)
  :returns "number"
  :doc "Maximum. Single list arg or variadic.")

(define-primitive "clamp"
  :params (x lo hi)
  :returns "number"
  :doc "Clamp x to range [lo, hi]."
  :body (max lo (min hi x)))

(define-primitive "inc"
  :params (n)
  :returns "number"
  :doc "Increment by 1."
  :body (+ n 1))

(define-primitive "dec"
  :params (n)
  :returns "number"
  :doc "Decrement by 1."
  :body (- n 1))


;; --------------------------------------------------------------------------
;; Comparison
;; --------------------------------------------------------------------------

(define-primitive "="
  :params (a b)
  :returns "boolean"
  :doc "Equality (value equality, not identity).")

(define-primitive "!="
  :params (a b)
  :returns "boolean"
  :doc "Inequality."
  :body (not (= a b)))

(define-primitive "<"
  :params (a b)
  :returns "boolean"
  :doc "Less than.")

(define-primitive ">"
  :params (a b)
  :returns "boolean"
  :doc "Greater than.")

(define-primitive "<="
  :params (a b)
  :returns "boolean"
  :doc "Less than or equal.")

(define-primitive ">="
  :params (a b)
  :returns "boolean"
  :doc "Greater than or equal.")


;; --------------------------------------------------------------------------
;; Predicates
;; --------------------------------------------------------------------------

(define-primitive "odd?"
  :params (n)
  :returns "boolean"
  :doc "True if n is odd."
  :body (= (mod n 2) 1))

(define-primitive "even?"
  :params (n)
  :returns "boolean"
  :doc "True if n is even."
  :body (= (mod n 2) 0))

(define-primitive "zero?"
  :params (n)
  :returns "boolean"
  :doc "True if n is zero."
  :body (= n 0))

(define-primitive "nil?"
  :params (x)
  :returns "boolean"
  :doc "True if x is nil/null/None.")

(define-primitive "number?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a number (int or float).")

(define-primitive "string?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a string.")

(define-primitive "list?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a list/array.")

(define-primitive "dict?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a dict/map.")

(define-primitive "empty?"
  :params (coll)
  :returns "boolean"
  :doc "True if coll is nil or has length 0.")

(define-primitive "contains?"
  :params (coll key)
  :returns "boolean"
  :doc "True if coll contains key. Strings: substring check. Dicts: key check. Lists: membership.")


;; --------------------------------------------------------------------------
;; Logic
;; --------------------------------------------------------------------------

(define-primitive "not"
  :params (x)
  :returns "boolean"
  :doc "Logical negation. Note: and/or are special forms (short-circuit).")


;; --------------------------------------------------------------------------
;; Strings
;; --------------------------------------------------------------------------

(define-primitive "str"
  :params (&rest args)
  :returns "string"
  :doc "Concatenate all args as strings. nil → empty string, bool → true/false.")

(define-primitive "concat"
  :params (&rest colls)
  :returns "list"
  :doc "Concatenate multiple lists into one. Skips nil values.")

(define-primitive "upper"
  :params (s)
  :returns "string"
  :doc "Uppercase string.")

(define-primitive "lower"
  :params (s)
  :returns "string"
  :doc "Lowercase string.")

(define-primitive "trim"
  :params (s)
  :returns "string"
  :doc "Strip leading/trailing whitespace.")

(define-primitive "split"
  :params (s &rest sep)
  :returns "list"
  :doc "Split string by separator (default space).")

(define-primitive "join"
  :params (sep coll)
  :returns "string"
  :doc "Join collection items with separator string.")

(define-primitive "replace"
  :params (s old new)
  :returns "string"
  :doc "Replace all occurrences of old with new in s.")

(define-primitive "slice"
  :params (coll start &rest end)
  :returns "any"
  :doc "Slice a string or list from start to end (exclusive). End is optional.")

(define-primitive "starts-with?"
  :params (s prefix)
  :returns "boolean"
  :doc "True if string s starts with prefix.")

(define-primitive "ends-with?"
  :params (s suffix)
  :returns "boolean"
  :doc "True if string s ends with suffix.")


;; --------------------------------------------------------------------------
;; Collections — construction
;; --------------------------------------------------------------------------

(define-primitive "list"
  :params (&rest args)
  :returns "list"
  :doc "Create a list from arguments.")

(define-primitive "dict"
  :params (&rest pairs)
  :returns "dict"
  :doc "Create a dict from key/value pairs: (dict :a 1 :b 2).")

(define-primitive "range"
  :params (start end &rest step)
  :returns "list"
  :doc "Integer range [start, end) with optional step.")


;; --------------------------------------------------------------------------
;; Collections — access
;; --------------------------------------------------------------------------

(define-primitive "get"
  :params (coll key &rest default)
  :returns "any"
  :doc "Get value from dict by key, or list by index. Optional default.")

(define-primitive "len"
  :params (coll)
  :returns "number"
  :doc "Length of string, list, or dict.")

(define-primitive "first"
  :params (coll)
  :returns "any"
  :doc "First element, or nil if empty.")

(define-primitive "last"
  :params (coll)
  :returns "any"
  :doc "Last element, or nil if empty.")

(define-primitive "rest"
  :params (coll)
  :returns "list"
  :doc "All elements except the first.")

(define-primitive "nth"
  :params (coll n)
  :returns "any"
  :doc "Element at index n, or nil if out of bounds.")

(define-primitive "cons"
  :params (x coll)
  :returns "list"
  :doc "Prepend x to coll.")

(define-primitive "append"
  :params (coll x)
  :returns "list"
  :doc "Append x to end of coll (returns new list).")

(define-primitive "chunk-every"
  :params (coll n)
  :returns "list"
  :doc "Split coll into sub-lists of size n.")

(define-primitive "zip-pairs"
  :params (coll)
  :returns "list"
  :doc "Consecutive pairs: (1 2 3 4) → ((1 2) (2 3) (3 4)).")


;; --------------------------------------------------------------------------
;; Collections — dict operations
;; --------------------------------------------------------------------------

(define-primitive "keys"
  :params (d)
  :returns "list"
  :doc "List of dict keys.")

(define-primitive "vals"
  :params (d)
  :returns "list"
  :doc "List of dict values.")

(define-primitive "merge"
  :params (&rest dicts)
  :returns "dict"
  :doc "Merge dicts left to right. Later keys win. Skips nil.")

(define-primitive "assoc"
  :params (d &rest pairs)
  :returns "dict"
  :doc "Return new dict with key/value pairs added/overwritten.")

(define-primitive "dissoc"
  :params (d &rest keys)
  :returns "dict"
  :doc "Return new dict with keys removed.")

(define-primitive "into"
  :params (target coll)
  :returns "any"
  :doc "Pour coll into target. List target: convert to list. Dict target: convert pairs to dict.")


;; --------------------------------------------------------------------------
;; Format helpers
;; --------------------------------------------------------------------------

(define-primitive "format-date"
  :params (date-str fmt)
  :returns "string"
  :doc "Parse ISO date string and format with strftime-style format.")

(define-primitive "format-decimal"
  :params (val &rest places)
  :returns "string"
  :doc "Format number with fixed decimal places (default 2).")

(define-primitive "parse-int"
  :params (val &rest default)
  :returns "number"
  :doc "Parse string to integer with optional default on failure.")


;; --------------------------------------------------------------------------
;; Text helpers
;; --------------------------------------------------------------------------

(define-primitive "pluralize"
  :params (count &rest forms)
  :returns "string"
  :doc "Pluralize: (pluralize 1) → \"\", (pluralize 2) → \"s\". Or (pluralize n \"item\" \"items\").")

(define-primitive "escape"
  :params (s)
  :returns "string"
  :doc "HTML-escape a string (&, <, >, \", ').")

(define-primitive "strip-tags"
  :params (s)
  :returns "string"
  :doc "Remove HTML tags from string.")


;; --------------------------------------------------------------------------
;; Date & parsing helpers
;; --------------------------------------------------------------------------

(define-primitive "parse-datetime"
  :params (s)
  :returns "string"
  :doc "Parse datetime string — identity passthrough (returns string or nil).")

(define-primitive "split-ids"
  :params (s)
  :returns "list"
  :doc "Split comma-separated ID string into list of trimmed non-empty strings.")


;; --------------------------------------------------------------------------
;; CSSX — style system primitives
;; --------------------------------------------------------------------------

(define-primitive "css"
  :params (&rest atoms)
  :returns "style-value"
  :doc "Resolve style atoms to a StyleValue with className and CSS declarations.
        Atoms are keywords or strings: (css :flex :gap-4 :hover:bg-sky-200).")

(define-primitive "merge-styles"
  :params (&rest styles)
  :returns "style-value"
  :doc "Merge multiple StyleValues into one combined StyleValue.")
