lib/mixpanel/http/hackney.ex

if Code.ensure_loaded?(:hackney) do
  defmodule Mixpanel.HTTP.Hackney do
    @moduledoc """
    Adapter for [hackney](https://github.com/benoitc/hackney).

    Remember to add `{:hackney, "~> 1.20"}` to dependencies (and `:hackney` to applications in `mix.exs`).

    ## Examples

    ```
    # set globally in config/config.exs
    config :mixpanel_api_ex, :http_adapter, Mixpanel.HTTP.Hackney
    ```

    ## Adapter specific options

    - `:max_body_length` - Max response body size in bytes. Actual response may
      be bigger because hackney stops after the last chunk that surpasses
      `:max_body_length`. Defaults to `:infinity`.
    """

    @behaviour Mixpanel.HTTP

    @impl Mixpanel.HTTP
    @spec get(url :: String.t(), headers :: [{String.t(), binary}], opts :: keyword) ::
            {:ok, status :: 200..599, headers :: [{String.t(), binary}], body :: term}
            | {:error, String.t()}
    def get(url, headers, opts) do
      request(:get, url, headers, "", opts)
    end

    @impl Mixpanel.HTTP
    @spec post(
            url :: String.t(),
            body :: binary,
            headers :: [{String.t(), binary}],
            opts :: keyword
          ) ::
            {:ok, status :: 200..599, headers :: [{String.t(), binary}], body :: term}
            | {:error, String.t()}
    def post(url, body, headers, opts) do
      request(:post, url, headers, body, opts)
    end

    defp request(method, url, headers, body, opts) do
      opts =
        opts
        |> Keyword.split([:insecure])
        |> then(fn {opts, _} -> opts end)
        |> Enum.reduce([], fn
          {:insecure, true}, acc ->
            [:insecure | acc]
        end)

      case :hackney.request(method, url, headers, body, opts) do
        {:ok, status_code, headers} ->
          {:ok, status_code, headers, <<>>}

        {:ok, status_code, headers, client} ->
          max_length = Keyword.get(opts, :max_body_length, :infinity)

          case :hackney.body(client, max_length) do
            {:ok, body} ->
              {:ok, status_code, headers, body}

            {:error, reason} ->
              {:error, to_string(reason)}
          end

        {:ok, {:maybe_redirect, _status_code, _headers, _client}} ->
          {:error, "Redirect not supported"}

        {:error, reason} ->
          {:error, to_string(reason)}
      end
    end
  end
end