;; lib/commerce/recon.sx — reconciliation as relational queries over the ledger.
;;
;; The ledger (ledger.sx) is the source of truth; reconciliation projects it
;; into per-order summary tuples and then asks miniKanren questions about them.
;; "Which orders are overpaid?" / "which order settled to net N?" are backward
;; queries (run*) over the same relation, not separate code paths.
;;
;; A summary tuple is positional:
;;   (order-stream total paid refunded net status)
;; net = paid - refunded; status = :unpaid|:ok|:underpaid|:overpaid.

(define
  order-summary
  (fn
    (b stream)
    (let
      ((events (persist/read b stream)))
      (let
        ((total (order-total-of events))
          (paid (order-paid-amount-of events))
          (refunded (order-refunded-amount-of events)))
        (list
          stream
          total
          paid
          refunded
          (- paid refunded)
          (order-recon-of events))))))

(define
  ledger-summaries
  (fn (b) (map (fn (s) (order-summary b s)) (persist/backend-streams b))))

;; --- relations over the summary set ---

(define
  summaryo
  (fn
    (summaries id total paid refunded net status)
    (membero (list id total paid refunded net status) summaries)))

(define
  recon-statuso
  (fn
    (summaries id status)
    (fresh (t p r n) (summaryo summaries id t p r n status))))

(define
  neto
  (fn
    (summaries id net)
    (fresh (t p r status) (summaryo summaries id t p r net status))))

;; A mismatch is any order whose money does not reconcile (over or under).
(define
  mismatcho
  (fn
    (summaries id)
    (fresh
      (status)
      (recon-statuso summaries id status)
      (conde ((== status :underpaid)) ((== status :overpaid))))))

;; --- deterministic query helpers (run* over the live ledger) ---

(define
  orders-with-status
  (fn (b status) (run* id (recon-statuso (ledger-summaries b) id status))))

(define overpaid-orders (fn (b) (orders-with-status b :overpaid)))
(define underpaid-orders (fn (b) (orders-with-status b :underpaid)))
(define settled-orders (fn (b) (orders-with-status b :ok)))
(define unpaid-orders (fn (b) (orders-with-status b :unpaid)))

(define
  mismatched-orders
  (fn (b) (run* id (mismatcho (ledger-summaries b) id))))

;; Backward: which order(s) settled to a given net amount?
(define
  orders-with-net
  (fn (b net) (run* id (neto (ledger-summaries b) id net))))

;; Total signed discrepancy across the ledger (net - total over paid orders);
;; 0 when every settled order reconciles exactly.
(define
  ledger-discrepancy
  (fn
    (b)
    (reduce
      (fn
        (acc s)
        (let
          ((status (nth s 5)))
          (if
            (= status :unpaid)
            acc
            (+ acc (- (nth s 4) (nth s 1))))))
      0
      (ledger-summaries b))))
