;; lib/mod/audit.sx — append-only decision log.
;;
;; Every decision the api commits is recorded as an immutable audit entry holding
;; the decision (action + matching rule), the proof tree (the derivation that
;; justified it), and a snapshot of the evidence in force at decision time. The
;; log is append-only: entries are never mutated or removed, only appended, each
;; with a monotonic sequence number. Retrieval is by report id (full history) or
;; by sequence.

(define mod/*audit-log* (list))
(define mod/*audit-seq* 0)

(define
  mod/audit-reset!
  (fn
    ()
    (begin (set! mod/*audit-log* (list)) (set! mod/*audit-seq* 0))))

(define mod/mk-audit-entry (fn (seq decision evidence-snapshot) {:action (get decision :action) :evidence evidence-snapshot :proof (get decision :proof) :rule (get decision :rule) :report-id (get decision :report-id) :seq seq}))

(define
  mod/log-decision!
  (fn
    (decision evidence-snapshot)
    (begin
      (set! mod/*audit-seq* (+ mod/*audit-seq* 1))
      (let
        ((entry (mod/mk-audit-entry mod/*audit-seq* decision evidence-snapshot)))
        (begin (append! mod/*audit-log* entry) entry)))))

;; entries for one report, in chronological (sequence) order
(define
  mod/audit
  (fn
    (id)
    (reduce
      (fn
        (acc e)
        (if (= (get e :report-id) id) (append acc (list e)) acc))
      (list)
      mod/*audit-log*)))

(define mod/audit-all (fn () mod/*audit-log*))
(define mod/audit-count (fn () (len mod/*audit-log*)))

;; most recent decision logged for a report (nil if none)
(define
  mod/audit-latest
  (fn
    (id)
    (reduce
      (fn (acc e) (if (= (get e :report-id) id) e acc))
      nil
      mod/*audit-log*)))
