;; lib/mod/fed.sx — federation: cross-instance reports, decision sharing, trust,
;; revocation. fed-sx itself is mocked here (an in-memory outbox); the real wire
;; transport would replace mod/fed-send!.
;;
;; Trust is advisory by default (the hard rule): a peer's decision only binds
;; locally when (mod/trusted? peer :mod) holds. An untrusted peer's decision is
;; recorded as a suggestion in the advisory log and is NOT applied. Local
;; decisions propagate outward via the outbox. Revocation undoes a locally
;; applied action when its proof is invalidated, notifying the origin peer.

(define mod/*fed-trust* (list))     ;; {:peer :scope}
(define mod/*fed-outbox* (list))    ;; {:to :type :payload}
(define mod/*fed-advisory* (list))  ;; {:peer :decision} — received, not applied
(define mod/*fed-applied* (list))   ;; {:report-id :action :origin :revoked}
(define mod/*fed-origins* (list))   ;; {:id :origin}

(define
  mod/fed-reset!
  (fn
    ()
    (begin
      (set! mod/*fed-trust* (list))
      (set! mod/*fed-outbox* (list))
      (set! mod/*fed-advisory* (list))
      (set! mod/*fed-applied* (list))
      (set! mod/*fed-origins* (list)))))

;; ── trust model ──

(define
  mod/trust-match?
  (fn
    (t peer scope)
    (if (= (get t :peer) peer) (= (get t :scope) scope) false)))

(define
  mod/grant-trust
  (fn (peer scope) (begin (append! mod/*fed-trust* {:scope scope :peer peer}) true)))

(define
  mod/revoke-trust
  (fn
    (peer scope)
    (set!
      mod/*fed-trust*
      (reduce
        (fn
          (acc t)
          (if (mod/trust-match? t peer scope) acc (append acc (list t))))
        (list)
        mod/*fed-trust*))))

(define
  mod/trusted?
  (fn
    (peer scope)
    (mod/any? (fn (t) (mod/trust-match? t peer scope)) mod/*fed-trust*)))

;; ── cross-instance reports ──

(define
  mod/fed-receive-report
  (fn
    (peer by about reason)
    (let
      ((r (mod/report by about reason)))
      (begin (append! mod/*fed-origins* {:id (mod/report-id r) :origin peer}) r))))

(define
  mod/report-origin
  (fn
    (id)
    (reduce
      (fn (acc o) (if (= (get o :id) id) (get o :origin) acc))
      "local"
      mod/*fed-origins*)))

;; ── decision sharing (mock fed-sx send) ──

(define
  mod/fed-send!
  (fn (to type payload) (begin (append! mod/*fed-outbox* {:type type :to to :payload payload}) true)))

(define mod/fed-outbox (fn () mod/*fed-outbox*))

(define
  mod/fed-share-decision
  (fn
    (decision peers)
    (reduce
      (fn
        (acc p)
        (begin (mod/fed-send! p "decision" decision) (append acc (list p))))
      (list)
      peers)))

;; ── receiving a peer's decision (advisory unless trusted) ──

(define
  mod/fed-applied-action
  (fn
    (report-id)
    (reduce
      (fn (acc a) (if (= (get a :report-id) report-id) a acc))
      nil
      mod/*fed-applied*)))

(define
  mod/fed-receive-decision
  (fn
    (peer decision)
    (if
      (mod/trusted? peer :mod)
      (begin (append! mod/*fed-applied* {:revoked false :action (get decision :action) :report-id (get decision :report-id) :origin peer}) {:advisory false :peer peer :applied true :decision decision})
      (begin (append! mod/*fed-advisory* {:peer peer :decision decision}) {:advisory true :peer peer :applied false :decision decision}))))

;; ── revocation ──

(define
  mod/fed-revoke!
  (fn
    (report-id reason)
    (begin
      (set!
        mod/*fed-applied*
        (map
          (fn (a) (if (= (get a :report-id) report-id) {:revoked true :action (get a :action) :report-id (get a :report-id) :origin (get a :origin)} a))
          mod/*fed-applied*))
      (mod/fed-send! (mod/report-origin report-id) "revocation" {:report-id report-id :reason reason})
      report-id)))

;; re-run the engine; if the action no longer holds, the prior decision's proof
;; is invalidated — revoke the applied moderation.
(define
  mod/fed-revoke-if-invalidated
  (fn
    (report decision reports rules)
    (let
      ((d2 (mod/decide-report report reports rules)))
      (if
        (= (get d2 :action) (get decision :action))
        {:revoked false :decision d2}
        (begin
          (mod/fed-revoke! (get decision :report-id) "proof invalidated")
          {:revoked true :decision d2})))))
