;; lib/commerce/catalog.sx — catalog snapshot + relational accessors.
;;
;; A catalog snapshot is an immutable dict:
;;   {:products (list (list sku price class) ...)
;;    :variants (list (list sku variant delta) ...)
;;    :stock    (list (list sku variant qty) ...)}
;;
;; Money is integer minor units (pence/cents). class is a keyword product
;; class consumed later by tax and promotion relations. delta is a signed
;; price adjustment for a variant; qty is on-hand stock for (sku,variant).
;;
;; Accessor relations take the snapshot as the first argument and are fully
;; multidirectional: (producto cat "widget" p c) binds p,c forward;
;; (producto cat s 1000 c) enumerates every sku priced 1000 backward.

(define empty-catalog {:products (list) :stock (list) :variants (list)})

(define make-catalog (fn (products variants stock) {:products products :stock stock :variants variants}))

(define cat-products (fn (cat) (get cat :products)))
(define cat-variants (fn (cat) (get cat :variants)))
(define cat-stock (fn (cat) (get cat :stock)))

;; --- core fact relations ---

(define
  producto
  (fn
    (cat sku price class)
    (membero (list sku price class) (get cat :products))))

(define
  varianto
  (fn
    (cat sku variant delta)
    (membero (list sku variant delta) (get cat :variants))))

(define
  stocko
  (fn
    (cat sku variant qty)
    (membero (list sku variant qty) (get cat :stock))))

;; --- derived relations ---

(define
  priceo
  (fn (cat sku price) (fresh (c) (producto cat sku price c))))

(define
  classo
  (fn (cat sku class) (fresh (p) (producto cat sku p class))))

;; Effective unit price of a (sku,variant): base + variant delta.
(define
  unit-priceo
  (fn
    (cat sku variant price)
    (fresh
      (base delta)
      (priceo cat sku base)
      (varianto cat sku variant delta)
      (pluso-i base delta price))))

;; --- deterministic lookups (first solution under fixed fact order) ---

(define
  catalog-price
  (fn
    (cat sku)
    (let
      ((rs (run 1 p (priceo cat sku p))))
      (if (empty? rs) nil (first rs)))))

(define
  catalog-class
  (fn
    (cat sku)
    (let
      ((rs (run 1 c (classo cat sku c))))
      (if (empty? rs) nil (first rs)))))

(define catalog-has? (fn (cat sku) (not (nil? (catalog-price cat sku)))))
