lib/baz_venue_adapter_open_sea/fetch_collection_event_page_by_slug.ex

defmodule BazVenueAdapterOpenSea.FetchCollectionEventPageBySlug do
  @moduledoc """
  Implementation of the Baz.VenueAdapter.fetch_collection_event_page_by_slug/4 callback
  """

  defmodule Input do
    defstruct ~w[slug token_ids page_cursor page_limit]a
  end

  alias BazVenueAdapterOpenSea.Credentials
  alias BazVenueAdapterOpenSea.ApiClient

  @type venue :: Baz.VenueAdapter.venue()
  @type slug :: Baz.VenueAdapter.collection_slug()
  @type token_ids :: Baz.VenueAdapter.token_ids()
  @type page_cursor :: Baz.Page.cursor()
  @type result :: Baz.VenueAdapter.fetch_collection_event_page_by_slug_result()

  @default_page_limit 300

  @spec fetch_collection_event_page_by_slug(venue, slug, token_ids, page_cursor) :: result
  def fetch_collection_event_page_by_slug(venue, slug, token_ids, page_cursor) do
    input = %Input{
      slug: slug,
      token_ids: token_ids,
      page_cursor: page_cursor,
      page_limit: @default_page_limit
    }

    {venue, input}
    |> with_api_key()
    |> fetch()
    |> write_to_raw_sinks()
    |> build_changset()
  end

  defp with_api_key({venue, input}) do
    case Credentials.get_api_key(venue.credentials) do
      {:ok, api_key} -> {venue, input, api_key}
      {:error, :not_found} -> {:error, :api_key_not_found}
    end
  end

  defp fetch({venue, input, api_key}) do
    params = build_params(input)
    api_result = ApiClient.fetch_collection_event_page_by_slug(params, api_key)
    {venue, input, api_key, api_result}
  end

  defp fetch({:error, _reason} = error), do: error

  defp write_to_raw_sinks({venue, input, _api_key, api_result} = args) do
    :ok = Baz.Sinks.write_raw(:collection_events, venue, input, api_result)
    args
  end

  defp write_to_raw_sinks({:error, _reason} = error), do: error

  defp build_changset({venue, input, _api_key, api_result}) do
    case api_result do
      {:ok, cursor, _} ->
        events = build_events(venue, input.slug, cursor)
        %Baz.Page{data: events, next_page_cursor: cursor.next}

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

  defp build_changset({:error, _reason} = error), do: error

  defp build_params(input) do
    p = %{collection_slug: input.slug, limit: input.page_limit}
    p = if input.token_ids != nil, do: Map.put(p, :token_ids, input.token_ids), else: p
    if input.page_cursor != nil, do: Map.put(p, :cursor, input.page_cursor), else: p
  end

  defp build_events(venue, slug, cursor) do
    cursor.asset_events
    |> Enum.map(fn venue_event ->
      {venue, slug, venue_event}
      |> build_event_attrs()
      |> Baz.CollectionEvents.collection_event_changeset()
    end)
  end

  defp build_event_attrs({venue, slug, venue_event}) do
    %{
      venue: venue.name,
      slug: slug,
      token_id: venue_event.asset["token_id"],
      source_id: "#{venue_event.id}",
      event_type: venue_event.event_type,
      event_timestamp: venue_event.event_timestamp
    }
  end
end