;; lib/mod/engine.sx — decide a report by querying the policy program.
;;
;; build-program assembles the report's facts plus the compiled policy clauses;
;; decide-report runs the Prolog query and returns a decision. A decision is a
;; proof, not a bare keyword: it carries the matching rule, the conditions it
;; required, the evidence that satisfied them, and a derivation — the proof tree.
;;
;; The proof tree is built constructively: for the matching rule, each body goal
;; is re-queried against the same DB with the report id bound, recording the goal
;; text, whether it was solved, and the bindings that satisfied it. That is a
;; genuine derivation drawn from the Prolog database, ready for the audit trail.

(define
  mod/find-rule
  (fn
    (rules name)
    (reduce
      (fn
        (acc r)
        (if (nil? acc) (if (= (mod/rule-name r) name) r acc) acc))
      nil
      rules)))

(define
  mod/build-program
  (fn
    (r count rules)
    (str (mod/report-facts r count) "\n" (mod/rules->program rules))))

(define
  mod/proof-goals
  (fn
    (db id conds)
    (if
      (empty? conds)
      (list {:solved true :goal "true" :bindings {}})
      (map
        (fn
          (c)
          (let
            ((g (mod/cond->goal c id)))
            (let ((sols (pl-query-all db g))) {:solved (if (empty? sols) false true) :goal g :bindings (if (empty? sols) {} (first sols))})))
        conds))))

(define
  mod/decide-report
  (fn
    (r reports rules)
    (let
      ((count (mod/report-count (mod/report-about r) reports))
        (kinds (mod/classify-keywords r))
        (id (mod/report-id r)))
      (let
        ((program (mod/build-program r count rules)))
        (let
          ((db (pl-load program)))
          (let
            ((sol (pl-query-one db (str "policy_action(" id ", Action, Rule)"))))
            (if
              (nil? sol)
              {:action "keep" :proof {:goals (list) :evidence kinds :conditions (list) :rule "none" :count count} :report-id id :rule "none"}
              (let
                ((rname (dict-get sol "Rule")))
                (let ((rule (mod/find-rule rules rname))) {:action (mod/rule-action rule) :proof {:goals (mod/proof-goals db id (mod/rule-when rule)) :evidence kinds :conditions (mod/rule-when rule) :rule rname :count count} :report-id id :rule rname})))))))))
