;; lib/mod/tests/disjunction.sx — Ext 15: disjunctive (:any) conditions.

(define mod-or-count 0)
(define mod-or-pass 0)
(define mod-or-fail 0)
(define mod-or-failures (list))

(define
  mod-or-test!
  (fn
    (name got expected)
    (begin
      (set! mod-or-count (+ mod-or-count 1))
      (if
        (= got expected)
        (set! mod-or-pass (+ mod-or-pass 1))
        (begin
          (set! mod-or-fail (+ mod-or-fail 1))
          (append!
            mod-or-failures
            (str name "\n    expected: " expected "\n    got:      " got)))))))

;; one rule, OR of two classifications → one action covers both
(define
  mod-or-rules
  (list
    (mod/mk-rule
      "spam-or-abuse-hide"
      :hide (list
        (list
          :any (list (list :classification "spam") (list :classification "abuse")))))
    (mod/mk-rule "default-keep" :keep (list))))

(define mod-or-spam (mod/mk-report "r1" "a" "b" "this is spam"))
(define mod-or-abuse (mod/mk-report "r2" "a" "b" "harassment here"))
(define mod-or-clean (mod/mk-report "r3" "a" "b" "a fine post"))

(mod-or-test!
  "OR: spam branch → hide"
  (get
    (mod/decide-report mod-or-spam (list mod-or-spam) mod-or-rules)
    :action)
  "hide")
(mod-or-test!
  "OR: abuse branch → hide"
  (get
    (mod/decide-report mod-or-abuse (list mod-or-abuse) mod-or-rules)
    :action)
  "hide")
(mod-or-test!
  "OR: neither branch → keep"
  (get
    (mod/decide-report mod-or-clean (list mod-or-clean) mod-or-rules)
    :action)
  "keep")

;; ── goal text + proof ──

(mod-or-test!
  "cond->goal :any joins with ;"
  (mod/cond->goal
    (list
      :any (list (list :classification "spam") (list :classification "abuse")))
    "Id")
  "(classification(Id, spam) ; classification(Id, abuse))")

(define
  mod-or-dec
  (mod/decide-report mod-or-spam (list mod-or-spam) mod-or-rules))
(mod-or-test!
  "OR proof goal solved"
  (get (first (get (get mod-or-dec :proof) :goals)) :solved)
  true)
(mod-or-test!
  "OR proof goal text"
  (get (first (get (get mod-or-dec :proof) :goals)) :goal)
  "(classification(r1, spam) ; classification(r1, abuse))")

;; ── :any composes with :not (NOR-ish) and :attr ──

(define
  mod-or-mixed-rules
  (list
    (mod/mk-rule
      "spam-or-flagged-hide"
      :hide (list
        (list
          :any (list (list :classification "spam") (list :attr "flagged")))))
    (mod/mk-rule "default-keep" :keep (list))))

(define
  mod-or-flagged
  (mod/attach-attr (mod/mk-report "r4" "a" "b" "a fine post") "flagged"))
(mod-or-test!
  "OR over classification|attr: flagged clean post → hide"
  (get
    (mod/decide-report
      mod-or-flagged
      (list mod-or-flagged)
      mod-or-mixed-rules)
    :action)
  "hide")

(mod-or-test!
  "cond->goal :any with :not branch"
  (mod/cond->goal
    (list
      :any (list
        (list :classification "spam")
        (list :not (list :attr "verified"))))
    "Id")
  "(classification(Id, spam) ; not(attr(Id, verified)))")

;; AND still works alongside OR in the same :when list
(define
  mod-or-and-rules
  (list
    (mod/mk-rule
      "spam-and-not-verified"
      :hide (list
        (list
          :any (list (list :classification "spam") (list :classification "abuse")))
        (list :not (list :attr "verified"))))
    (mod/mk-rule "default-keep" :keep (list))))

(define
  mod-or-spam-verified
  (mod/attach-attr (mod/mk-report "r5" "a" "b" "this is spam") "verified"))
(mod-or-test!
  "AND of OR + NOT: verified spam → keep"
  (get
    (mod/decide-report
      mod-or-spam-verified
      (list mod-or-spam-verified)
      mod-or-and-rules)
    :action)
  "keep")
(mod-or-test!
  "AND of OR + NOT: unverified abuse → hide"
  (get
    (mod/decide-report mod-or-abuse (list mod-or-abuse) mod-or-and-rules)
    :action)
  "hide")

(define mod-disjunction-tests-run! (fn () {:failures mod-or-failures :total mod-or-count :passed mod-or-pass :failed mod-or-fail}))
