lib/baz_venue_adapter_open_sea/fetch_collection_asset_page_by_slug.ex

defmodule BazVenueAdapterOpenSea.FetchCollectionAssetPageBySlug do
  @moduledoc """
  Implementation of the Baz.VenueAdapter.fetch_collection_asset_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_asset_page_by_slug_result()

  @default_page_limit 200

  @spec fetch_collection_asset_page_by_slug(venue, slug, token_ids, page_cursor) :: result
  def fetch_collection_asset_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_asset_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_assets, 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, _} ->
        assets = build_assets(venue, input.slug, cursor)
        %Baz.Page{data: assets, 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_assets(venue, slug, cursor) do
    cursor.assets
    |> Enum.map(fn venue_asset ->
      {venue, slug, venue_asset}
      |> build_asset_attrs()
      |> Baz.CollectionAssets.collection_asset_changeset()
    end)
  end

  defp build_asset_attrs({venue, slug, venue_asset}) do
    # TODO: enumerate collection asset traits and save
    %{
      venue: venue.name,
      slug: slug,
      token_id: venue_asset.token_id,
      name: venue_asset.name,
      background_color: venue_asset.background_color,
      image_uri: venue_asset.image_url,
      image_preview_uri: venue_asset.image_preview_url,
      image_thumbnail_uri: venue_asset.image_thumbnail_url,
      image_original_uri: venue_asset.image_original_url,
      animation_uri: venue_asset.animation_url,
      animation_original_uri: venue_asset.animation_original_url,
      is_nsfw: venue_asset.is_nsfw
    }
  end
end