defmodule Transport.SIRIQueryGenerator.Behaviour do
  @moduledoc """
  A behaviour to ensure we can Mox the calls to generate a query and
  put assertions on them.
  """
  @callback generate_query(map()) :: binary()
end

defmodule Transport.SIRIQueryGenerator do
  @moduledoc """
  A bridge to help convert HTML-form-based queries into method calls.
  """
  def impl, do: Application.get_env(:transport, :siri_query_generator_impl, __MODULE__)

  @behaviour Transport.SIRIQueryGenerator.Behaviour

  @doc """
  A loosely-typed (via map) entry point to ensure we can generically convert
  input parameters (such as those generated by a form). This makes sure this code
  is mox'able and testable.
  """
  @impl Transport.SIRIQueryGenerator.Behaviour
  def generate_query(params) do
    # extract baseline items (always required)
    %{
      template: template,
      requestor_ref: requestor_ref,
      message_id: message_id,
      timestamp: timestamp
    } = params

    method_name = Macro.underscore(template)

    # careful, we do not want to invoke a user-provided method blindly!
    # make sure to verify the method beforehand.
    cond do
      method_name == "get_estimated_timetable" ->
        line_refs = params |> Map.fetch!(:line_refs)

        apply(Transport.SIRI, method_name |> String.to_atom(), [
          timestamp,
          requestor_ref,
          message_id,
          line_refs
        ])

      method_name == "get_stop_monitoring" ->
        apply(Transport.SIRI, method_name |> String.to_atom(), [
          timestamp,
          requestor_ref,
          message_id,
          Map.fetch!(params, :stop_ref)
        ])

      method_name in [
        "check_status",
        "lines_discovery",
        "stop_points_discovery",
        "get_general_message"
      ] ->
        apply(Transport.SIRI, method_name |> String.to_atom(), [
          timestamp,
          requestor_ref,
          message_id
        ])
    end
  end
end
