defmodule Absinthe.GraphqlWS.Util do
  @moduledoc """
  Helper functions that are imported into a module using `Absinthe.GraphqlWS.Socket`.
  """

  alias Absinthe.GraphqlWS.Socket

  @doc """
  Implementation copied from `Phoenix.Socket`, but changed to match on a `Absinthe.GraphqlWS.Socket` struct.
  """
  @spec assign(Socket.t(), atom(), term()) :: Socket.t()
  @spec assign(Socket.t(), map() | keyword()) :: Socket.t()
  def assign(%Socket{} = socket, key, value),
    do: assign(socket, [{key, value}])

  def assign(%Socket{} = socket, attrs) when is_map(attrs) or is_list(attrs),
    do: %{socket | assigns: Map.merge(socket.assigns, Map.new(attrs))}

  @doc """
  Adds key-value pairs into Absinthe context. This can be used in the `c:Absinthe.GraphqlWS.Socket.handle_init/2`
  to assign keys/values to the current Absinthe context.

  ## Examples

      defmodule MySocket do
        use Absinthe.GraphqlWS.Socket, schema: MySchema

        def handle_init(%{"user_id" => user_id}) do
          user = find_user(user_id)
          socket = assign_context(socket, current_user: user)
          %{:reply, :ok, {:text, Absinthe.GraphqlWS.Message.ConnectionAck.new()}, socket}
        end
      end
  """
  def assign_context(%Socket{absinthe: absinthe} = socket, context) do
    context =
      absinthe
      |> Map.fetch!(:opts)
      |> Keyword.fetch!(:context)
      |> Map.merge(Map.new(context))

    put_options(socket, context: context)
  end

  @doc """
  Same as `assign_context/2` except one key-value pair is assigned.
  """
  def assign_context(socket, key, value) do
    assign_context(socket, [{key, value}])
  end

  @doc """
  Sets the options for a given GraphQL document execution.

  ## Examples

      iex> put_options(socket, context: %{current_user: user})
      %Socket{}
  """
  def put_options(socket, opts) do
    opts = Keyword.merge(socket.absinthe.opts, opts)
    absinthe = %{socket.absinthe | opts: opts}
    %{socket | absinthe: absinthe}
  end

  @doc """
  Retrieves the JSON library module used to encode and decode transport messages
  """
  def json_library, do: Application.get_env(:absinthe_graphql_ws, :json_library, Jason)
end
