defmodule Aprsme.Swoosh.ResendAdapter do
  @moduledoc false
  @behaviour Swoosh.Adapter

  @api_url "https://api.resend.com/emails"

  @impl true
  def deliver(%Swoosh.Email{} = email, config) do
    api_key = Keyword.fetch!(config, :api_key)

    body =
      %{
        subject: email.subject,
        from: format_sender(email.from),
        to: format_recipients(email.to),
        bcc: format_recipients(email.bcc),
        cc: format_recipients(email.cc),
        reply_to: format_recipients(email.reply_to),
        headers: email.headers,
        html: email.html_body,
        text: email.text_body
      }
      |> Enum.reject(fn {_k, v} -> is_nil(v) or v == [] or v == %{} end)
      |> Map.new()

    req_opts = maybe_put_plug([json: body, headers: [{"authorization", "Bearer #{api_key}"}]], config)

    case Req.post(@api_url, req_opts) do
      {:ok, %{status: status, body: %{"id" => id}}} when status in 200..299 ->
        {:ok, %{id: id}}

      {:ok, %{body: %{"name" => name, "message" => message}}} ->
        {:error, {name, message}}

      {:ok, %{body: body}} ->
        {:error, body}

      {:error, reason} ->
        {:error, reason}
    end
  end

  @impl true
  def deliver_many(emails, config) do
    Enum.reduce_while(emails, {:ok, []}, fn email, {:ok, acc} ->
      case deliver(email, config) do
        {:ok, result} -> {:cont, {:ok, acc ++ [result]}}
        {:error, _} = error -> {:halt, error}
      end
    end)
  end

  @impl true
  def validate_config(config) do
    if Keyword.has_key?(config, :api_key) do
      :ok
    else
      {:error, "Missing required :api_key configuration for Aprsme.Swoosh.ResendAdapter"}
    end
  end

  defp maybe_put_plug(opts, config) do
    case Keyword.get(config, :plug) do
      nil -> opts
      plug -> Keyword.put(opts, :plug, plug)
    end
  end

  defp format_sender(nil), do: nil
  defp format_sender(from) when is_binary(from), do: from
  defp format_sender({"", from}), do: from
  defp format_sender({name, from}), do: "#{name} <#{from}>"

  defp format_recipients(nil), do: nil
  defp format_recipients(r) when is_binary(r), do: r
  defp format_recipients({_name, addr}), do: addr
  defp format_recipients(list) when is_list(list), do: Enum.map(list, &format_recipients/1)
end
