%% @doc
%%  Lambda: API for publishing servers and turning modules into clients.
%% @end
-module(lambda).
-compile(warn_missing_spec).
-author("").

%% API
-export([
    publish/1,
    publish/2,
    discover/1,
    discover/2,
    deploy/2
]).

-type meta() :: #{
    md5 := binary(),
    exports := [{atom(), pos_integer()}],
    attributes => [term()]
}.

-type dst() :: atom() | pid() | {local, atom()} | {global, term()} | {'via', Module :: module(), Name :: term()}.

  Location : pid or { registered_process , node }
-type location() :: pid() | {atom(), node()}.

-export_type([meta/0, dst/0, location/0]).

%%--------------------------------------------------------------------
  @doc Discovers a module , and starts a PLB for that module under lambda supervision .
-spec discover(module()) -> {ok, pid()} | ignore.
discover(Module) ->
    discover(Module, #{capacity => erlang:system_info(schedulers)}).

-spec discover(module(), lambda_plb:options()) -> {ok, pid()} | ignore.
discover(Module, Options) ->
    case erlang:module_loaded(Module) of
        true ->
            %% module is available locally
            ignore;
        false ->
            {ok, Plb} = lambda_client_sup:start_plb(Module, Options),
            _ = lambda_plb:meta(Plb),
            {ok, Plb}
    end.

%% @doc Publishes a module, starting server under lambda supervision.
-spec publish(module()) -> {ok, pid()} | {error, {already_started, pid()}}.
publish(Module) ->
    publish(Module, #{capacity => erlang:system_info(schedulers)}).

-spec publish(module(), lambda_listener:options()) -> {ok, pid()} | {error, {already_started, pid()}}.
publish(Module, Options) ->
    lambda_listener_sup:start_listener(Module, Options).

%% @doc Deploys a module, either new one, or upgrading existing service.
-spec deploy(module(), #{}) -> {ok, pid()} | {error, {already_exists, pid()}}.
deploy(Module, Options) ->
    true = Module =/= Options,
    {ok, self()}.

 @doc
  Lambda: API for publishing servers and turning modules into clients.
 @end
 API
--------------------------------------------------------------------
 module is available locally
 @doc Publishes a module, starting server under lambda supervision.
 @doc Deploys a module, either new one, or upgrading existing service.
-module(lambda).
-compile(warn_missing_spec).
-author("").

-export([
    publish/1,
    publish/2,
    discover/1,
    discover/2,
    deploy/2
]).

-type meta() :: #{
    md5 := binary(),
    exports := [{atom(), pos_integer()}],
    attributes => [term()]
}.

-type dst() :: atom() | pid() | {local, atom()} | {global, term()} | {'via', Module :: module(), Name :: term()}.

  Location : pid or { registered_process , node }
-type location() :: pid() | {atom(), node()}.

-export_type([meta/0, dst/0, location/0]).

  @doc Discovers a module , and starts a PLB for that module under lambda supervision .
-spec discover(module()) -> {ok, pid()} | ignore.
discover(Module) ->
    discover(Module, #{capacity => erlang:system_info(schedulers)}).

-spec discover(module(), lambda_plb:options()) -> {ok, pid()} | ignore.
discover(Module, Options) ->
    case erlang:module_loaded(Module) of
        true ->
            ignore;
        false ->
            {ok, Plb} = lambda_client_sup:start_plb(Module, Options),
            _ = lambda_plb:meta(Plb),
            {ok, Plb}
    end.

-spec publish(module()) -> {ok, pid()} | {error, {already_started, pid()}}.
publish(Module) ->
    publish(Module, #{capacity => erlang:system_info(schedulers)}).

-spec publish(module(), lambda_listener:options()) -> {ok, pid()} | {error, {already_started, pid()}}.
publish(Module, Options) ->
    lambda_listener_sup:start_listener(Module, Options).

-spec deploy(module(), #{}) -> {ok, pid()} | {error, {already_exists, pid()}}.
deploy(Module, Options) ->
    true = Module =/= Options,
    {ok, self()}.
