defmodule Spotify.Artist do
  @moduledoc """
    Functions for retrieving information about artists and for
    managing a user’s followed artists.

    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/artist-endpoints/
  """

  import Spotify.Helpers
  use Spotify.Responder

  alias Spotify.{
    Artist,
    Client,
    Paging,
    Track
  }

  defstruct ~w[
    external_urls
    followers
    genres
    href
    id
    images
    name
    popularity
    type
    uri
  ]a

  @doc """
  Get Spotify catalog information for a single artist identified by their unique Spotify ID.
  [Spotify Documenation](https://developer.spotify.com/web-api/get-artist/)

  **Method**: `GET`

      Spotify.Artist.get_artist(conn, "4")
      # => { :ok, %Artist{}}
  """
  def get_artist(conn, id) do
    url = get_artist_url(id)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get Spotify catalog information for a single artist identified by their unique Spotify ID.

      iex> Spotify.Artist.get_artist_url("4")
      "https://api.spotify.com/v1/artists/4"
  """
  def get_artist_url(id) do
    "https://api.spotify.com/v1/artists/#{id}"
  end

  @doc """
  Get Spotify catalog information for several artists based on their Spotify IDs.
  [Spotify Documenation](https://developer.spotify.com/web-api/get-several-artists/)

  **Method**: `GET`

  **Required Params**: `ids`

      Spotify.Artist.get_artists(conn, ids: "1,4")
      # => { :ok, [%Artist{}, ...] }
  """
  def get_artists(conn, params = [ids: _ids]) do
    url = get_artists_url(params)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get Spotify catalog information for several artists based on their Spotify IDs.

      iex> Spotify.Artist.get_artists_url(ids: "1,4")
      "https://api.spotify.com/v1/artists?ids=1%2C4"
  """
  def get_artists_url(params) do
    "https://api.spotify.com/v1/artists" <> query_string(params)
  end

  @doc """
  Get Spotify catalog information about an artist’s top tracks by country.
  [Spotify Documenation](https://developer.spotify.com/web-api/get-artists-top-tracks/)

  **Method**: `GET`

  **Required Params**: `country`

      Spotify.get_top_tracks(conn, "4", country: "US")
      # => { :ok, [%Track{}, ...] }
  """
  def get_top_tracks(conn, id, params) do
    url = get_top_tracks_url(id, params)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get Spotify catalog information about an artist’s top tracks by country.

      iex> Spotify.Artist.get_top_tracks_url("4", country: "US")
      "https://api.spotify.com/v1/artists/4/top-tracks?country=US"
  """
  def get_top_tracks_url(id, params) do
    "https://api.spotify.com/v1/artists/#{id}/top-tracks" <> query_string(params)
  end

  @doc """
  Get Spotify catalog information about artists similar to a given artist.
  [Spotify Documenation](https://developer.spotify.com/web-api/get-related-artists/)

  ** Method **: `GET`

      Spotify.Artist.get_related_artists(conn, "4")
      # => { :ok, [ %Artist{}, ... ] }
  """
  def get_related_artists(conn, id) do
    url = get_related_artists_url(id)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
    Get Spotify catalog information about artists similar to a given artist.

        iex> Spotify.Artist.get_related_artists_url("4")
        "https://api.spotify.com/v1/artists/4/related-artists"
  """
  def get_related_artists_url(id) do
    "https://api.spotify.com/v1/artists/#{id}/related-artists"
  end

  @doc """
  Get the current user’s followed artists.

  **Method**: `GET`

  **Optional Params**: `type`, `limit`, `after`

      Spotify.Artist.artists_I_follow_url(conn)
      # => { :ok, %Paging{items: [%Artist{}, ...]} }
  """
  def artists_I_follow(conn, params \\ []) do
    url = artists_I_follow_url(params)
    conn |> Client.get(url) |> handle_response
  end

  @doc """
  Get the current user’s followed artists.

      iex> Spotify.Artist.artists_I_follow_url(limit: 5)
      "https://api.spotify.com/v1/me/following?type=artist&limit=5"
  """
  def artists_I_follow_url(params) do
    "https://api.spotify.com/v1/me/following?type=artist&" <> URI.encode_query(params)
  end

  @doc """
  Implements the hook expected by the Responder behaviour
  """
  def build_response(body) do
    case body do
      %{"artists" => %{"items" => _items}} = response -> build_paged_artists(response)
      %{"artists" => artists} -> build_artists(artists)
      %{"tracks" => tracks} -> Track.build_tracks(tracks)
      %{"name" => _} -> to_struct(Artist, body)
      booleans_or_error -> booleans_or_error
    end
  end

  @doc false
  defp build_paged_artists(%{"artists" => response}) do
    %Paging{
      items: build_artists(response["items"]),
      next: response["next"],
      total: response["total"],
      cursors: response["cursors"],
      limit: response["limit"],
      href: response["href"]
    }
  end

  @doc false
  def build_artists(artists) do
    Enum.map(artists, &to_struct(Artist, &1))
  end
end
