;; identity/registry.sx — routes sessions by id and by (subject, client).
;;
;; The registry is the directory that makes SSO possible: one subject can
;; hold many sessions (one per client), and the OAuth machine asks it the
;; single question that drives silent login — \"is there a live session
;; for this subject + this client?\". It stores (SessionId, Subject,
;; Client, Pid) rows and answers:
;;
;;   whereis_session(Id)      -> {ok, Pid} | {error, not_found}
;;   lookup(Subject, Client)  -> {ok, Pid} | {error, not_found}   (SSO probe)
;;   sessions_for(Subject)    -> {ok, [SessionId, ...]}           (fan-out)
;;
;; The registry only routes — it holds no grant state and decides nothing.
;; Liveness of the routed-to session is that session process's own affair.

(define
  identity-registry-source
  "-module(identity_registry).\n\n   start() ->\n     spawn(fun () -> loop([]) end).\n\n   register(Reg, SessionId, Subject, Client, Pid) ->\n     Reg ! {register, SessionId, Subject, Client, Pid, self()},\n     receive {registry_reply, R} -> R end.\n\n   whereis_session(Reg, SessionId) ->\n     Reg ! {whereis_session, SessionId, self()},\n     receive {registry_reply, R} -> R end.\n\n   lookup(Reg, Subject, Client) ->\n     Reg ! {lookup, Subject, Client, self()},\n     receive {registry_reply, R} -> R end.\n\n   sessions_for(Reg, Subject) ->\n     Reg ! {sessions_for, Subject, self()},\n     receive {registry_reply, R} -> R end.\n\n   deregister(Reg, SessionId) ->\n     Reg ! {deregister, SessionId, self()},\n     receive {registry_reply, R} -> R end.\n\n   stop(Reg) ->\n     Reg ! {stop, self()},\n     receive {registry_reply, R} -> R end.\n\n   loop(Entries) ->\n     receive\n       {register, SessionId, Subject, Client, Pid, From} ->\n         From ! {registry_reply, ok},\n         loop([{SessionId, Subject, Client, Pid} | remove_id(SessionId, Entries)]);\n       {whereis_session, SessionId, From} ->\n         From ! {registry_reply, find_id(SessionId, Entries)},\n         loop(Entries);\n       {lookup, Subject, Client, From} ->\n         From ! {registry_reply, find_sc(Subject, Client, Entries)},\n         loop(Entries);\n       {sessions_for, Subject, From} ->\n         From ! {registry_reply, {ok, collect_subject(Subject, Entries)}},\n         loop(Entries);\n       {deregister, SessionId, From} ->\n         From ! {registry_reply, ok},\n         loop(remove_id(SessionId, Entries));\n       {stop, From} ->\n         From ! {registry_reply, ok}\n     end.\n\n   find_id(_, []) -> {error, not_found};\n   find_id(Id, [{Sid, _, _, Pid} | Rest]) ->\n     case Sid =:= Id of\n       true -> {ok, Pid};\n       false -> find_id(Id, Rest)\n     end.\n\n   find_sc(_, _, []) -> {error, not_found};\n   find_sc(Subject, Client, [{_, Su, Cl, Pid} | Rest]) ->\n     case Su =:= Subject of\n       true ->\n         case Cl =:= Client of\n           true -> {ok, Pid};\n           false -> find_sc(Subject, Client, Rest)\n         end;\n       false -> find_sc(Subject, Client, Rest)\n     end.\n\n   collect_subject(_, []) -> [];\n   collect_subject(Subject, [{Sid, Su, _, _} | Rest]) ->\n     case Su =:= Subject of\n       true -> [Sid | collect_subject(Subject, Rest)];\n       false -> collect_subject(Subject, Rest)\n     end.\n\n   remove_id(_, []) -> [];\n   remove_id(Id, [{Sid, Su, Cl, Pid} | Rest]) ->\n     case Sid =:= Id of\n       true -> remove_id(Id, Rest);\n       false -> [{Sid, Su, Cl, Pid} | remove_id(Id, Rest)]\n     end.")

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