defmodule Tai.VenueAdapters.Bitmex.AmendOrder do
  alias Tai.Orders.Responses

  @type credentials :: Tai.Venues.Adapter.credentials()
  @type order :: Tai.Orders.Order.t()
  @type attrs :: Tai.Orders.Worker.amend_attrs()
  @type response :: Responses.AmendAccepted.t()
  @type reason ::
          :timeout
          | :overloaded
          | {:nonce_not_increasing, msg :: String.t()}
          | {:unhandled, term}

  @spec amend_order(order, attrs, credentials) :: {:ok, response} | {:error, reason}
  def amend_order(order, attrs, credentials) do
    params = to_params(attrs, order.venue_order_id)

    credentials
    |> to_venue_credentials
    |> send_to_venue(params)
    |> parse_response()
  end

  defdelegate send_to_venue(credentials, params),
    to: ExBitmex.Rest.Orders,
    as: :amend

  defdelegate to_venue_credentials(credentials),
    to: Tai.VenueAdapters.Bitmex.Credentials,
    as: :from

  defp to_params(attrs, venue_order_id) do
    attrs
    |> Enum.reduce(
      %{},
      fn
        {:price, v}, p -> p |> Map.put(:price, v)
        {:qty, v}, p -> p |> Map.put(:leavesQty, v)
        _, p -> p
      end
    )
    |> Map.put(:orderID, venue_order_id)
  end

  defp parse_response({:ok, venue_order, _rate_limit}) do
    {:ok, venue_timestamp, 0} = DateTime.from_iso8601(venue_order.timestamp)
    received_at = Tai.Time.monotonic_time()

    response = %Responses.AmendAccepted{
      id: venue_order.order_id,
      received_at: received_at,
      venue_timestamp: venue_timestamp
    }

    {:ok, response}
  end

  defp parse_response({:error, :timeout, nil}) do
    {:error, :timeout}
  end

  defp parse_response({:error, :connect_timeout, nil}) do
    {:error, :connect_timeout}
  end

  defp parse_response({:error, :overloaded, _}) do
    {:error, :overloaded}
  end

  defp parse_response({:error, :rate_limited, _}) do
    {:error, :rate_limited}
  end

  defp parse_response({:error, {:nonce_not_increasing, _} = reason, _}) do
    {:error, reason}
  end

  defp parse_response({:error, reason, _}) do
    {:error, {:unhandled, reason}}
  end
end
