const qs = require('querystring');
const Album = require('../structures/Album.js');
const Artist = require('../structures/Artist.js');
const Track = require('../structures/Track.js');

const API = 'https://api.spotify.com/v1/artists';
const HTTPError = require('../HTTPError.js');
const ApiError = require('../ApiError.js');

class ArtistManager {
  /**
   * Manages spotify artist.
   * @param {Spotify} Spotify - The spotify client.
   */
  constructor(spotify) {
    /**
     * The spotify client.
     * @type {Spotify}
     */
    this.spotify = spotify;
  }

  /**
   * Get Spotify catalog information for a single artist identified by their unique Spotify ID.
   * @param {string} id - The Spotify ID of the artist.
   * @returns {Promise<Artist|HTTPError|ApiError>}
   */
  get(id) {
    const path = API + '/' + id;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (body) {
              if (response.status == 200) {
                const artist = new Artist(this.spotify, body);
                return resolve(artist);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Get Spotify catalog information about an artist's albums.
   * @param {string} id - The Spotify ID of the artist.
   * @param {ArtistAlbumsOptions} options
   * @returns {Promise<Album[]|HTTPError|ApiError>}
   */
  /* prettier-ignore */
  albums(id, {
    groups = ['album', 'single', 'appears_on', 'compilation'],
    limit = 20,
    offset = 0,
  } = {}) {
    const options = qs.stringify({
      include_groups: Array.isArray(groups) ? groups.join(',') : groups,
      limit,
      offset,
    });

    const path = API + '/' + id + '/albums?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then(body => {
            if (body) {
              if (response.status == 200) {
                const albums = body.items.map(
                  (a) => new Album(this.spotify, a)
                );
                return resolve(albums);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Add the current user as a follower of one or more artists.
   * @param {string|string[]} ids - The Spotify ID of the artist.
   * @returns {Promise<Status|HTTPError|ApiError>}
   */
  follow(ids) {
    const options = qs.stringify({
      ids: Array.isArray(ids) ? ids.join(',') : ids,
      type: 'artist',
    });

    const path = 'https://api.spotify.com/v1/me/following?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
          method: 'put',
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (response.status == 204) {
              resolve({ status: response.status });
            } else if (body) {
              reject(new ApiError(body.error));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Remove the current user as a follower of one or more artists.
   * @param {string|string[]} ids - The Spotify ID of the artist.
   * @returns {Promise<Status|HTTPError|ApiError>}
   */
  unfollow(ids) {
    const options = qs.stringify({
      ids: Array.isArray(ids) ? ids.join(',') : ids,
      type: 'artist',
    });

    const path = 'https://api.spotify.com/v1/me/following?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
          method: 'delete',
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (response.status == 200) {
              resolve({ status: response.status });
            } else if (body) {
              reject(new ApiError(body.error));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Check to see if the current user is following one or more artists.
   * @param {string} ids - The Spotify ID of the artist.
   * @param {string|string[]} users - A list of Spotify User IDs.
   * @returns {boolean[]|HTTPError|ApiError}
   */
  following(ids) {
    const options = qs.stringify({
      ids: Array.isArray(ids) ? ids.join(',') : ids,
      type: 'artist',
    });

    const path = 'https://api.spotify.com/v1/me/following/contains?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (body) {
              if (response.status == 200) {
                return resolve(body);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Get Spotify catalog information about an artist's top tracks by country.
   * @param {string} id - The Spotify ID of the artist.
   * @param {string} country - An ISO 3166-1 alpha-2 country code.
   * @returns {Promise<Track[]|HTTPError|ApiError>}
   */
  top(id, country) {
    const options = qs.stringify({
      country,
    });

    const path = API + '/' + id + '/top-tracks?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (body) {
              if (response.status == 200) {
                const tracks = body.tracks.map(
                  (t) => new Track(this.spotify, t)
                );
                return resolve(tracks);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Get Spotify catalog information about artists similar to a given artist.
   * @param {string} id - The Spotify ID of the artist.
   * @returns {Promise<Artist[]|HTTPError|ApiError>}
   */
  related(id) {
    const path = API + '/' + id + '/related-artists';

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (body) {
              if (response.status == 200) {
                const artists = body.artists.map(
                  (a) => new Artist(this.spotify, a)
                );
                return resolve(artists);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }

  /**
   * Get Spotify catalog information about artists.
   * @param {string} query - Your search query.
   * @param {SearchOptions} options
   * @returns {Promise<Artist[]|HTTPError|ApiError>}
   */
  search(query, { external = false, limit = 20, offset = 0 } = {}) {
    const opts = {
      q: query,
      type: 'artist',
      limit,
      offset,
    };

    if (external) {
      opts['include_external'] = 'audio';
    }

    const options = qs.stringify(opts);
    const path = 'https://api.spotify.com/v1/search?' + options;

    return new Promise((resolve, reject) => {
      this.spotify.util
        .fetch({
          path,
        })
        .then((response) => {
          this.spotify.util.toJson(response).then((body) => {
            if (body) {
              if (response.status == 200) {
                const artists = body.artists.items.map(
                  (a) => new Artist(this.spotify, a)
                );
                return resolve(artists);
              }
              reject(new ApiError(response));
            }
            reject(new HTTPError(response));
          });
        });
    });
  }
}

module.exports = ArtistManager;

/**
 * The list of groups for an album by default all are supplied.
 * - album
 * - single
 * - appears_on
 * - compilation
 * @typedef {string} AlbumGroups
 */

/**
 * @typedef {Object} ArtistAlbumsOptions
 * @param {AlbumGroups[]} [groups] - A list of keywords that will be used to filter the response.
 * @param {number} [limit=20] - The maximum number of items to return. Minimum: 1. Maximum: 50.
 * @param {number} [offset=0] - The index of the first item to return. Use with limit to get the next set of items.
 */