defmodule Spotify.Personalization do
  @moduledoc """
  Endpoints for retrieving information about the user’s listening habits

  Some endpoints return collections. Spotify wraps the collection in a paging object,
  this API does the same. A single piece of data will not be wrapped.

  There are two functions for each endpoint, one that actually makes the request,
  and one that provides the endpoint:

        Spotify.Playist.create_playlist(conn, "foo", "bar") # makes the POST request.
        Spotify.Playist.create_playlist_url("foo", "bar") # provides the url for the request.

  https://developer.spotify.com/web-api/web-api-personalization-endpoints/
  """

  import Spotify.Helpers
  use Spotify.Responder

  alias Spotify.{
    Client,
    Paging
  }

  defstruct ~w[
    items
    next
    previous
    total
    limit
    href
  ]a

  @doc """
  Get the current user’s top artists based on calculated affinity.
  [Spotify Documentation](https://developer.spotify.com/web-api/get-users-top-artists-and-tracks/)

  **Method**: `GET`

  **Optional Params**: `limit`, `offset`, `time_range`

      Spotify.Personalization.top_artists(conn)
      { :ok, artists: [%Spotify.Artist{..}...], paging: %Paging{next:...} }
  """
  def top_artists(conn, params \\ []) do
    url = top_artists_url(params)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get the current user’s top artists based on calculated affinity.

      iex> Spotify.Personalization.top_artists_url(limit: 5, time_range: "medium_term")
      "https://api.spotify.com/v1/me/top/artists?limit=5&time_range=medium_term"
  """
  def top_artists_url(params \\ []) do
    url() <> "artists" <> query_string(params)
  end

  @doc """
  Get the current user’s top tracks based on calculated affinity.
  [Spotify Documentation](https://developer.spotify.com/web-api/get-users-top-artists-and-tracks/)

  **Method**: `GET`

  **Optional Params**: `limit`, `offset`, `time_range`

      Spotify.Personalization.top_tracks(conn)
      { :ok, tracks: [%Spotify.Tracks{..}...], paging: %Paging{next:...} }

  """
  def top_tracks(conn, params \\ []) do
    url = top_tracks_url(params)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get the current user’s top tracks based on calculated affinity.

      iex> Spotify.Personalization.top_tracks_url
      "https://api.spotify.com/v1/me/top/tracks"
  """
  def top_tracks_url(params \\ []) do
    url() <> "tracks" <> query_string(params)
  end

  @doc """
  Base URL
  """
  def url do
    "https://api.spotify.com/v1/me/top/"
  end

  @doc """
  Implements the hook expected by the Responder behaviour
  """
  def build_response(body) do
    items =
      Enum.map(body["items"], fn item ->
        case item["type"] do
          "artist" -> build_artist_struct(item)
          "track" -> build_track_struct(item)
        end
      end)

    paging = to_struct(Paging, body)
    Map.put(paging, :items, items)
  end

  @doc false
  def build_artist_struct(artist) do
    to_struct(Spotify.Artist, artist)
  end

  @doc false
  def build_track_struct(track) do
    to_struct(Spotify.Track, track)
  end
end
