# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

defmodule ElixirRuntime.Loop.Handler do
  @moduledoc """
  This module defines the Handler struct which is used to represent the
  module-function atom pair which identifies a client's entrypoint.
  """

  require Logger
  alias __MODULE__

  @enforce_keys [:module, :function]
  defstruct [
    :module,
    :function
  ]

  @doc """
  Manually create a handler from two atoms.
  ## Examples

    iex> ElixirRuntime.Loop.Handler.new(Elixir.Example, :handle)
    %ElixirRuntime.Loop.Handler{module: Elixir.Example, function: :handle}
  """
  def new(module, function) when is_atom(module) and is_atom(function) do
    %Handler{module: module, function: function}
  end

  @doc """
  Create a handler from the $_HANDLER environment variable.
  ## Examples

      iex> System.put_env("_HANDLER", "Elixir.Example:handle")
      iex> ElixirRuntime.Loop.Handler.configured()
      %ElixirRuntime.Loop.Handler{module: Elixir.Example, function: :handle}
  """
  def configured do
    [module, function] = handler_string() |> String.split(":", trim: true)
    new(String.to_atom(module), String.to_atom(function))
  end

  @doc """
  Invoke the handler function with the body as an argument.
  ## Examples

  Create a handler for String.trim and invoke it to get a result.
      iex> defmodule Example, do: def func(body, _context), do: body
      iex> handler = ElixirRuntime.Loop.Handler.new(Example, :func)
      iex> handler |> ElixirRuntime.Loop.Handler.invoke(%{msg: "hello"}, %{})
      %{msg: "hello"}
  """
  def invoke(%Handler{module: module, function: function}, body, context)
      when is_map(body) and is_map(context) do
    Logger.info("invoke #{module}.#{function}(#{Kernel.inspect(body)})")
    Kernel.apply(module, function, [body, context])
  end

  defp handler_string do
    raw = System.get_env("_HANDLER")
    Logger.debug("found handler string '#{raw}'")
    raw
  end
end
