-module(define_registry).
-export([fold/2, fold_fn/0, define_kind/1]).

%% Define-registry projection fold — Erlang-fun stand-in for the
%% genesis `define-registry.sx` body. The intent is identical: a
%% projection whose state is a registry-shaped property list, fed
%% by every `Create{Define*{...}}` activity. The SX body would
%% eventually replace this once an SX-source eval bridge lets the
%% kernel evaluate the genesis fold directly; until then this
%% Erlang module proves the meta-projection mechanism wires
%% through `projection:fold_fn` and `nx_kernel` cleanly.
%%
%% State shape mirrors `registry:new()` exactly:
%%   [{Kind, [{Name, Entry}, ...]}, ...]
%% so callers can use `registry:lookup/3` etc. on the result.
%%
%% Type discrimination uses atoms (`define_activity`, …). Real SX
%% would carry the string forms ("DefineActivity", …); the bridge
%% will translate. See define_kind/1 for the mapping.

fold(Activity, State) ->
    case envelope:get_field(type, Activity) of
        {ok, create} -> fold_create(Activity, State);
        _ -> State
    end.

fold_create(Activity, State) ->
    case envelope:get_field(object, Activity) of
        {ok, Obj} ->
            case envelope:get_field(type, Obj) of
                {ok, ObjType} ->
                    case define_kind(ObjType) of
                        not_a_define -> State;
                        Kind -> fold_register(Kind, Obj, State)
                    end;
                _ -> State
            end;
        _ -> State
    end.

fold_register(Kind, Obj, State) ->
    case envelope:get_field(name, Obj) of
        {ok, Name} ->
            case registry:register(Kind, Name, Obj, State) of
                {ok, NewState}     -> NewState;
                {error, unknown_kind} -> State
            end;
        not_found -> State
    end.

%% fold_fn/0 — a 2-arity Erlang fun the projection module plants
%% in its record's :fold slot. Lets `projection:start_link/3`
%% wire define-registry directly.
fold_fn() ->
    fun (Activity, State) -> fold(Activity, State) end.

%% define_kind/1 — discriminator from the inner Define* object's
%% :type atom to the registry kind atom. Anything unrecognised
%% returns not_a_define so the fold treats it as a pass-through.

define_kind(define_activity)   -> activity_types;
define_kind(define_object)     -> object_types;
define_kind(define_projection) -> projections;
define_kind(define_validator)  -> validators;
define_kind(define_codec)      -> codecs;
define_kind(define_sig_suite)  -> sig_suites;
define_kind(define_audience)   -> audience;
define_kind(_)                 -> not_a_define.
