;; lib/mod/trace.sx — policy dry-run diagnostics.
;;
;; decide-report returns the winning rule; a policy author debugging "why didn't
;; my rule fire?" needs the whole picture. mod/trace-rules evaluates a report
;; against every rule and reports each rule's proved/unproved status plus its
;; goal-by-goal derivation — so an unproved rule shows exactly which goal failed.
;; The winner is the first proved rule (same precedence as the engine).

(define
  mod/trace-rules
  (fn
    (r reports rules)
    (let
      ((count (mod/report-count (mod/report-about r) reports))
        (id (mod/report-id r)))
      (let
        ((db (pl-load (mod/build-program r count rules))))
        (let
          ((proved-names (map (fn (s) (dict-get s "Rule")) (pl-query-all db (str "policy_action(" id ", _, Rule)")))))
          (map
            (fn (rule) (let ((nm (mod/rule-name rule))) {:proved (mod/member? nm proved-names) :goals (mod/proof-goals db id (mod/rule-when rule)) :action (mod/rule-action rule) :rule nm}))
            rules))))))

(define
  mod/first-proved
  (fn
    (trace)
    (reduce
      (fn (acc t) (if (nil? acc) (if (get t :proved) t acc) acc))
      nil
      trace)))

(define
  mod/proved-rules
  (fn
    (trace)
    (reduce
      (fn
        (acc t)
        (if (get t :proved) (append acc (list (get t :rule))) acc))
      (list)
      trace)))

(define
  mod/trace-row
  (fn
    (t)
    (str
      (if (get t :proved) "[fires] " "[  -  ] ")
      (get t :rule)
      " → "
      (get t :action))))

(define
  mod/trace-report
  (fn (trace) (mod/join-with "\n" (map mod/trace-row trace))))
