;; identity/audit.sx — the grant audit ledger.
;;
;; Every transition that changes a grant — issue, refresh, revoke (and,
;; wired from oauth, consent) — appends an immutable event to this
;; append-only process. The ledger is queryable by subject, which is what
;; `(identity/audit subject)` answers. This is the in-memory realisation
;; of the event stream; a persist-backed stream is a later substrate
;; concern (Erlang↔persist bridge), kept out of scope here per the loop's
;; \"in-memory log until persist lands\" allowance — the queryable
;; semantics are identical.
;;
;; Events are {Seq, Subject, Action}; Seq is a monotonic sequence number.
;; Reads return chronological (oldest-first) order:
;;
;;   record(A, Subject, Action)  -> ok       (one-way; FIFO-ordered)
;;   audit(A, Subject)           -> [{Seq, Subject, Action}, ...]
;;   actions(A, Subject)         -> [Action, ...]
;;   count(A, Subject)           -> N
;;   all(A)                      -> [{Seq, Subject, Action}, ...]

(define
  identity-audit-source
  "-module(identity_audit).\n\n   start() ->\n     spawn(fun () -> loop([], 0) end).\n\n   record(A, Subject, Action) ->\n     A ! {event, Subject, Action},\n     ok.\n\n   audit(A, Subject) ->\n     A ! {audit, Subject, self()},\n     receive {audit_reply, R} -> R end.\n\n   actions(A, Subject) ->\n     A ! {actions, Subject, self()},\n     receive {audit_reply, R} -> R end.\n\n   count(A, Subject) ->\n     A ! {count, Subject, self()},\n     receive {audit_reply, R} -> R end.\n\n   all(A) ->\n     A ! {all, self()},\n     receive {audit_reply, R} -> R end.\n\n   loop(Events, Seq) ->\n     receive\n       {event, Subject, Action} ->\n         loop([{Seq, Subject, Action} | Events], Seq + 1);\n       {audit, Subject, From} ->\n         From ! {audit_reply, collect(Subject, Events, [])},\n         loop(Events, Seq);\n       {actions, Subject, From} ->\n         From ! {audit_reply, action_list(Subject, Events, [])},\n         loop(Events, Seq);\n       {count, Subject, From} ->\n         From ! {audit_reply, count_subj(Subject, Events, 0)},\n         loop(Events, Seq);\n       {all, From} ->\n         From ! {audit_reply, reverse(Events, [])},\n         loop(Events, Seq);\n       {stop, From} ->\n         From ! {audit_reply, ok}\n     end.\n\n   collect(_, [], Acc) -> Acc;\n   collect(Subject, [{Seq, S, A} | Rest], Acc) ->\n     case S =:= Subject of\n       true -> collect(Subject, Rest, [{Seq, S, A} | Acc]);\n       false -> collect(Subject, Rest, Acc)\n     end.\n\n   action_list(_, [], Acc) -> Acc;\n   action_list(Subject, [{_, S, A} | Rest], Acc) ->\n     case S =:= Subject of\n       true -> action_list(Subject, Rest, [A | Acc]);\n       false -> action_list(Subject, Rest, Acc)\n     end.\n\n   count_subj(_, [], N) -> N;\n   count_subj(Subject, [{_, S, _} | Rest], N) ->\n     case S =:= Subject of\n       true -> count_subj(Subject, Rest, N + 1);\n       false -> count_subj(Subject, Rest, N)\n     end.\n\n   reverse([], Acc) -> Acc;\n   reverse([H | T], Acc) -> reverse(T, [H | Acc]).")

(define
  identity-load-audit!
  (fn () (erlang-load-module identity-audit-source)))
