let axios = require('axios');
let utils = require('../utils');
let BaseClient = require('./base.client');

/**
 * Open Movie Database API Client.
 * @extends BaseClient
 */
class OMDBAPIClient extends BaseClient {
  /**
   * @param {string} apiKey The omdb api key ([Ref]{@link http://www.omdbapi.com/apikey.aspx})
   * @param baseUrl Base url for omdb api
     */
  constructor(apiKey, baseUrl) {
    super(apiKey, baseUrl);
  }
  /**
   * Get omdb client instance.
   * @param {string} apiKey The omdb api key
   * @returns {OMDBAPIClient}
   */
  static getInstance(apiKey) {
    return new OMDBAPIClient(apiKey, 'http://www.omdbapi.com/');
  }

  get authParam() {
    return 'apikey';
  }

  get requestKeyMap() {
    return {
      title: 't',
      query: 's',
      year: 'y',
      imdbID: 'i',
      plot: 'plot',
      format: 'r',
      type: 'type',
      page: 'page'
    };
  }

  /**
   * Get a media(movie/series) by looking up using different params (e.g. title, imdb id, year etc.)
   * @param {object} options The options based on which the movie will be fetched.
   * @example
   * get({title: 'Saw', year: 2004, plot: 'short', format: 'xml', type: 'movie'})
   * @example
   * get({imdbID: 'tt12444'})
   * @returns {Promise}
   * @fulfil {object} - The movie/series object.
   * @reject {Error} - The error with an appropriate `message`.
   */
  get(options) {
    if (!options.imdbID) {
      if (!options.title || !options.year || !options.type) {
        throw new Error('Either one of the format of data must be provided: imdbID | (title, year, type)');
      }
    }

    let reqOptions = this._translateIncomingRequestOptions(options);
    reqOptions.plot = reqOptions.plot || 'full';
    reqOptions.format = reqOptions.format || 'json';

    return this._makeHTTPGET('', reqOptions, null, this._transformResponse.bind(this))
      .then((resp) => {
        return resp.data;
      });
  }

  /**
   * Search for a movie/series.
   * @param {object} options The options based on which the search will be conducted.
   * @example
   * search({query: 'Saw', year: 2004, format: 'xml', type: 'movie'})
   * @returns {Promise}
   * @fulfil {object} - The movie/series object.
   * @reject {Error} - The error with an appropriate `message`.
   */
  search(options) {
    if (!options.query) {
      throw new Error('Query must be given');
    }

    let reqOptions = this._translateIncomingRequestOptions(options);
    reqOptions.plot = reqOptions.plot || 'full';
    reqOptions.format = reqOptions.format || 'json';
    return this._makeHTTPGET('', reqOptions, null, this._transformResponse.bind(this))
      .then((resp) => {
        return resp.data;
      });
  }

  /**
   * Get a media(movie/series) by title and year
   * @param {string} title Title of the media (movie/series)
   * @param {number} year The year media type(movie/series) was released (Optional).
   * @param {string} [type='movie'] Type of the media; movie/series (Optional).
   * @returns {Promise}
   * @fulfil {object} - The movie/series object.
   * @reject {Error} - The error with an appropriate `message`.
   */
  getByTitleAndYear(title, year, type = 'movie') {
    return this.get({title: title, year: year, format: 'json', plot: 'short', type: type});
  }

  /**
   * Get a media(movie/series) by IMDB ID
   * @param {string} imdbID IMDB ID of the media
   * @returns {Promise}
   * @fulfil {object} - The movie/series object.
   * @reject {Error} - The error with an appropriate `message`.
   */
  getByIMDBId(imdbID) {
    return this.get({imdbID: imdbID, format: 'json', plot: 'short'});
  }

  _transformResponse(data) {
    if (data['Search']) {
      return data['Search'].map((result) => {
        return this._translateResponseObject(result);
      });
    } else {
      return this._translateResponseObject(data);
    }
  }

  _translateResponseObject(responseObj) {
    let translated = {};
    Object.keys(responseObj).forEach((key) => {
      translated[utils.toCamelCase(key)] = responseObj[key];
    });
    return translated;
  }
}

module.exports = OMDBAPIClient;