var request = require('request');

var BROWSERSTACK_BROWSERS_ENDPOINT = 'https://www.browserstack.com/automate/browsers.json'


function annotate(browserList) {
  return browserList.map(function(browser) {
    browser.base = 'BrowserStack';
    browser._alias = 'bs_' + browser.browser;
    browser._version = parseFloat(browser.browser_version || '0');
    browser._id = [
      browser.browser,
      browser.browser_version,
      browser.device,
      browser.os,
      browser.os_version
    ].join('-').replace(/\s/g, '_');

    return browser;
  });
}


function versionAliases(browserList) {
  var ret = [];
  browserList.forEach(function(browser) {
    if (browser._version) {
      var copy = simpleCopy(browser);
      copy._alias = 'bs_' + browser.browser + '_' + browser._version;
      ret.push(copy);
    }
  });
  return ret;
}


var browsers = [];
var sortedByBrowser = {};
try {
  loadBrowsers(require('./browserstack.browsers.json'));
} catch (e) {
  console.error('Could not read ./browserstack.browsers.json. Ignoring.', e); // eslint-disable-line no-console
}


function simpleCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}


function loadBrowsers(browserListFromBrowserStack) {
  browsers = annotate(browserListFromBrowserStack);
  browsers = browsers.concat(versionAliases(browsers));

  sortedByBrowser = {};

  browsers.forEach(function(browser) {
    var browserList = sortedByBrowser[browser._alias] = sortedByBrowser[browser._alias] || [];
    browserList.push(browser);
  });

  // Sort all of the browsers by browser, _version
  for (var browser in sortedByBrowser) {
    var browserList = sortedByBrowser[browser];
    var numBrowsers = browserList.length;
    browserList.sort(function(a, b) {
      return a._version - b._version;
    });

    // Store a reference in the global browsers array to the oldest, previous and latest of
    // each browser.
    if (numBrowsers > 0) {
      var browserName = browser;
      var oldest = simpleCopy(browserList[0]);
      var latest = simpleCopy(browserList[browserList.length - 1]);
      var previous;

      oldest._alias = browserName + '_oldest';
      latest._alias = browserName + '_latest';

      browsers.push(oldest);
      browsers.push(latest);

      if (numBrowsers > 1) {
        previous = simpleCopy(browserList[browserList.length - 2]);
        previous._alias = browserName + '_previous';
        browsers.push(previous);
      }
    }
  }
}


function download(username, accessKey, callback) {
  var auth = 'Basic ' + new Buffer(username + ':' + accessKey).toString('base64');
  var req = {
    url: BROWSERSTACK_BROWSERS_ENDPOINT,
    headers: {
      Authorization: auth
    },
    json: true
  };

  request.get(req, function(err, resp, body) {
    if (err || resp.statusCode != 200) {
      return callback(err || new Error('Received non 200 response from BrowserStack: ' + resp.statusCode));
    }

    // Save the downloaded browsers in memory
    loadBrowsers(body);

    // TODO: Fix? fs is not defined here.
    // fs.writeFile('./browserstack.browsers.json', JSON.stringify(body, null, '  '), callback);
  });
}


/**
 * Returns an array of Objects that match the requested filters.
 * If no arguments are provided, an empty array is returned.
 *
 * E.g.
 *
 * filter('bs_latest') - Will return the latest browser Objects for each browser.
 * filter('bs_latest', 'bs_previous') - Will return the latest and previous browser Objects for each browser.
 * filter('bs_ie_latest', 'bs_oldest') - Will return the latest IE browser and all of the oldest browsers.
 * filter('bs_all') - Will return the all of the browser Objects.
 *
 * @returns {Array}
 */
function filter() {
  var ret = [];
  var labels = Array.prototype.slice.call(arguments);

  labels.forEach(function(label) {
    browsers.forEach(function(browser) {
      if (label === 'bs_all') {
        ret.push(browser);
      } else if (browser._alias.match(new RegExp(label.slice(2) + '$'))) {
        ret.push(browser);
      }
    });
  });

  return ret;
}


module.exports = {
  _browsers: browsers,
  download: download,
  filter: filter,
};
