defmodule Waffle.Actions.Delete do
  @moduledoc ~S"""
  Delete files from a defined adapter.

  After an object is stored through Waffle, you may optionally remove
  it.  To remove a stored object, pass the same path identifier and
  scope from which you stored the object.

      # Without a scope:
      {:ok, original_filename} = Avatar.store("/Images/me.png")
      :ok = Avatar.delete(original_filename)

      # With a scope:
      user = Repo.get!(User, 1)
      {:ok, original_filename} = Avatar.store({"/Images/me.png", user})
      # example 1
      :ok = Avatar.delete({original_filename, user})
      # example 2
      user = Repo.get!(User, 1)
      :ok = Avatar.delete({user.avatar, user})

  """

  alias Waffle.Actions.Delete

  defmacro __using__(_) do
    quote do
      def delete(args), do: Delete.delete(__MODULE__, args)

      defoverridable [{:delete, 1}]
    end
  end

  def delete(definition, {filepath, scope}) when is_binary(filepath) do
    do_delete(definition, {%{file_name: filepath}, scope})
  end

  def delete(definition, filepath) when is_binary(filepath) do
    do_delete(definition, {%{file_name: filepath}, nil})
  end

  #
  # Private
  #

  defp version_timeout do
    Application.get_env(:waffle, :version_timeout) || 15_000
  end

  defp do_delete(definition, {file, scope}) do
    if definition.async() do
      definition.__versions()
      |> Enum.map(fn(r)     -> async_delete_version(definition, r, {file, scope}) end)
      |> Enum.each(fn(task) -> Task.await(task, version_timeout()) end)
    else
      definition.__versions()
      |> Enum.each(fn(version) -> delete_version(definition, version, {file, scope}) end)
    end
    :ok
  end

  defp async_delete_version(definition, version, {file, scope}) do
    Task.async(fn -> delete_version(definition, version, {file, scope}) end)
  end

  defp delete_version(definition, version, {file, scope}) do
    conversion = definition.transform(version, {file, scope})
    if conversion == :skip do
      :ok
    else
      definition.__storage().delete(definition, version, {file, scope})
    end
  end
end
