defimpl Reactor.Dsl.Build, for: Ash.Reactor.Dsl.Action do
  @moduledoc false

  alias Ash.{Reactor.ActionStep, Resource.Info}
  alias Reactor.Builder
  alias Spark.{Dsl.Transformer, Error.DslError}

  import Ash.Reactor.BuilderUtils

  @doc false
  @impl true
  def build(action, reactor) do
    with {:ok, reactor, arguments} <- build_input_arguments(reactor, action) do
      arguments =
        arguments
        |> maybe_append(action.actor)
        |> maybe_append(action.tenant)
        |> maybe_append(action.context)
        |> Enum.concat(action.wait_for)

      action_options =
        action
        |> Map.take([:action, :authorize?, :domain, :resource, :undo, :undo_action])
        |> Enum.to_list()

      step_options =
        action
        |> Map.take([:async?, :guards])
        |> Map.put(:ref, :step_name)
        |> Enum.to_list()

      Builder.add_step(
        reactor,
        action.name,
        {ActionStep, action_options},
        arguments,
        step_options
      )
    end
  end

  @doc false
  @impl true
  def verify(action, _dsl_state) when action.undo == :never, do: :ok

  def verify(action, dsl_state) do
    case Info.action(action.resource, action.undo_action) do
      action when is_struct(action, Ash.Resource.Actions.Action) ->
        :ok

      nil ->
        {:error,
         DslError.exception(
           module: Transformer.get_persisted(dsl_state, :module),
           path: [:action, :undo_action],
           message:
             "No action found matching the name `#{action.undo_action}` on resource `#{inspect(action.resource)}`"
         )}

      _action ->
        {:error,
         DslError.exception(
           module: Transformer.get_persisted(dsl_state, :module),
           path: [:action, :undo_action],
           message: "The undo action for a generic action step should also be a generic action."
         )}
    end
  end
end
