a cabal implementation in erlang
-module(cabal_sup).
-behaviour(supervisor).

-export([start_link/1, stop/1, get_peer_pid/1]).
-export([init/1]).

%% Start the peer supervisor
%% Args:
%%   - {listener, [Host, Port]} - Address to listen on
%%   - {storage, Path} - Storage directory for database and keys
start_link(Args) ->
    case supervisor:start_link(?MODULE, {phase1, Args}) of
        {ok, SupPid} ->
            %% Phase 2: Get TransportSupPid and start peer_server
            TransportSupPid = get_child_pid(SupPid, cabal_transport_sup),

            StoragePath = proplists:get_value(storage, Args),

            %% Start peer_server with transport_sup
            PeerServerSpec = #{
                id => peer_server,
                start =>
                    {cabal, start_link, [
                        [
                            {transport_sup, TransportSupPid},
                            {storage, StoragePath}
                        ]
                    ]},
                restart => transient,
                shutdown => 5000,
                type => worker,
                modules => [cabal]
            },
            {ok, PeerServerPid} = supervisor:start_child(SupPid, PeerServerSpec),

            %% Phase 3: Start cabal_peer_events with peer_server info
            {ok, Db} = cabal:get_db(PeerServerPid),

            PeerEventsSpec = #{
                id => cabal_peer_events,
                start =>
                    {cabal_peer_events, start_link, [
                        [
                            {db, Db},
                            {transport_sup, TransportSupPid},
                            {peer_server_pid, PeerServerPid}
                        ]
                    ]},
                restart => transient,
                shutdown => 5000,
                type => worker,
                modules => [cabal_peer_events]
            },
            {ok, PeerEventsPid} = supervisor:start_child(SupPid, PeerEventsSpec),

            %% Connect peer_server to cabal_peer_events
            ok = cabal:update_peer_events_pid(PeerServerPid, PeerEventsPid),

            {ok, SupPid};
        Error ->
            Error
    end.

%% Stop the peer supervisor
stop(SupPid) ->
    %% Just send shutdown signal - let supervisor handle its children
    MRef = erlang:monitor(process, SupPid),
    unlink(SupPid),
    exit(SupPid, shutdown),
    %% Wait for supervisor to finish shutting down
    receive
        {'DOWN', MRef, process, SupPid, _Reason} ->
            ok
    end.

%% Get the peer server PID from the supervisor
get_peer_pid(SupPid) ->
    get_child_pid(SupPid, peer_server).

%% Supervisor callback
init({phase1, Args}) ->
    SupFlags = #{
        strategy => rest_for_one,
        intensity => 5,
        period => 10
    },

    %% Extract configuration
    [Host, WantPort] = proplists:get_value(listener, Args, ["0.0.0.0", 0]),
    StoragePath = proplists:get_value(storage, Args),

    %% Load transport keypair
    TransportKp = cabal:create_or_load_transport_keypair(StoragePath),

    %% Start cabal_transport_sup first (peer_server added dynamically)
    TransportSupSpec = #{
        id => cabal_transport_sup,
        start =>
            {cabal_transport_sup, start_link, [
                [
                    {listen_addr, {Host, WantPort}},
                    {key_pair, TransportKp},
                    % Set later via cabal_transport:register_handler
                    {event_handler, undefined}
                ]
            ]},
        restart => permanent,
        shutdown => infinity,
        type => supervisor,
        modules => [cabal_transport_sup]
    },

    {ok, {SupFlags, [TransportSupSpec]}}.

%% Internal helpers

get_child_pid(SupPid, ChildId) ->
    Children = supervisor:which_children(SupPid),
    case lists:keyfind(ChildId, 1, Children) of
        {ChildId, Pid, _, _} -> Pid;
        false -> undefined
    end.