lib/actors/actor/interface/http.ex

defmodule Actors.Actor.Interface.Http do
  use Actors.Actor.Interface
  require Logger

  alias Actors.{
    Actor.Entity.EntityState,
    Node.Client
  }

  alias Eigr.Functions.Protocol.Actors.{
    Actor,
    ActorState
  }

  alias Eigr.Functions.Protocol.{
    Context,
    ActorInvocation,
    ActorInvocationResponse
  }

  alias Google.Protobuf.Any

  @impl true
  def invoke_host(
        %ActorInvocation{actor_name: name, actor_system: system, command_name: command} = payload,
        %EntityState{
          actor: %Actor{state: actor_state}
        } = state,
        default_methods
      ) do
    if Enum.member?(default_methods, command) do
      current_state = Map.get(actor_state || %{}, :state)

      context =
        if is_nil(current_state),
          do: Context.new(state: Any.new()),
          else: Context.new(state: current_state)

      resp =
        ActorInvocationResponse.new(
          actor_name: name,
          actor_system: system,
          updated_context: context,
          value: current_state
        )

      {:ok, resp, state}
    else
      payload
      |> ActorInvocation.encode()
      |> Client.invoke_host_actor()
      |> case do
        {:ok, %Tesla.Env{body: ""}} ->
          Logger.error("User Function Actor response Invocation body is empty")
          {:error, :no_content, state}

        {:ok, %Tesla.Env{body: nil}} ->
          Logger.error("User Function Actor response Invocation body is nil")
          {:error, :no_content, state}

        {:ok, %Tesla.Env{body: body}} ->
          with %ActorInvocationResponse{
                 updated_context: %Context{} = user_ctx
               } = resp <- ActorInvocationResponse.decode(body) do
            {:ok, resp, update_state(state, user_ctx)}
          else
            error ->
              Logger.error("Error on parse response #{inspect(error)}")
              {:error, :invalid_content, state}
          end

        {:error, reason} ->
          Logger.error("User Function Actor Invocation Unknown Error: #{inspect(reason)}")
          {:error, reason, state}
      end
    end
  end

  defp update_state(
         %EntityState{
           actor: %Actor{} = _actor
         } = state,
         %Context{state: updated_state} = _user_ctx
       )
       when is_nil(updated_state),
       do: state

  defp update_state(
         %EntityState{
           actor: %Actor{state: actor_state} = _actor
         } = state,
         %Context{state: _updated_state} = _user_ctx
       )
       when is_nil(actor_state),
       do: state

  defp update_state(
         %EntityState{
           actor: %Actor{state: %ActorState{} = actor_state} = actor
         } = state,
         %Context{state: updated_state} = _user_ctx
       ) do
    new_state = %{actor_state | state: updated_state}
    %{state | actor: %{actor | state: new_state}}
  end
end