define([

'angular',
'lodash',
'config'

], function (angular, _, config) {

'use strict';

var module = angular.module('kibana.services');

module.service('esVersion', function($http, alertSrv, esMinVersion, $q, ejsResource) {

  this.versions = [];

  var ejs = ejsResource(config.elasticsearch);

  // save a reference to this
  var self = this,
    defer = $q.defer();

  this.init = function() {
    getVersions();
  };

  var getVersions = function() {
    if(self.versions.length !== 0) {
      defer.resolve(self.versions);
      return defer.promise;
    } else {
      var nodeInfo = ejs.client.get('/_nodes',
        undefined, undefined, function(data, status) {
        if(_.isUndefined(status)) {
          alertSrv.set('Error',"Could not contact Elasticsearch at "+ejs.client.server()+
            ". Please ensure that Elasticsearch is reachable from your system." ,'error');
        } else {
          alertSrv.set('Error',"Could not reach "+ejs.client.server()+"/_nodes. If you"+
          " are using a proxy, ensure it is configured correctly",'error');
        }
        return;
      });

      return nodeInfo.then(function(p) {
        _.each(p.nodes, function(v) {
          self.versions.push(v.version.split('-')[0]);
        });
        self.versions = sortVersions(_.uniq(self.versions));
        return self.versions;
      });
    }

  };

  // Get the max version in this cluster
  this.max = function(versions) {
    return _.last(versions);
  };

  // Return the lowest version in the cluster
  this.min = function(versions) {
    return _.first(versions);
  };

  // Sort versions from lowest to highest
  var sortVersions = function(versions) {
    var _versions = _.clone(versions),
      _r = [];

    while(_r.length < versions.length) {
      var _h = "0";
      /*jshint -W083 */
      _.each(_versions,function(v){
        if(self.compare(_h,v)) {
          _h = v;
        }
      });
      _versions = _.without(_versions,_h);
      _r.push(_h);
    }
    return _r.reverse();
  };

  /*
    Takes a version string with one of the following optional comparison prefixes: >,>=,<.<=
    and evaluates if the cluster meets the requirement. If the prefix is omitted exact match
    is assumed
  */
  this.is = function(equation) {
    var _v = equation,
      _cf;

    if(_v.charAt(0) === '>') {
      _cf = _v.charAt(1) === '=' ? self.gte(_v.slice(2)) : self.gt(_v.slice(1));
    } else if (_v.charAt(0) === '<') {
      _cf = _v.charAt(1) === '=' ? self.lte(_v.slice(2)) : self.lt(_v.slice(1));
    } else {
      _cf = self.eq(_v);
    }

    return _cf;
  };

  this.isMinimum = function() {
    return self.gte(esMinVersion);
  };

  // check if lowest version in cluster = `version`
  this.eq = function(version) {
    return getVersions().then(function(v) {
      return version === self.min(v) ? true : false;
    });

  };

  // version > lowest version in cluster?
  this.gt = function(version) {
    return getVersions().then(function(v) {
      return version === self.min(v) ? false : self.gte(version);
    });

  };

  // version < highest version in cluster?
  this.lt = function(version) {
    return getVersions().then(function(v) {
      return version === self.max(v) ? false : self.lte(version);
    });

  };

  // Check if the lowest version in the cluster is >= to `version`
  this.gte = function(version) {
    return getVersions().then(function(v) {
      return self.compare(version,self.min(v));
    });

  };

  // Check if the highest version in the cluster is <= to `version`
  this.lte = function(version) {
    return getVersions().then(function(v) {
      return self.compare(self.max(v),version);
    });
  };

  // Determine if a specific version is greater than or equal to another
  this.compare = function (required,installed) {
    if(_.isUndefined(installed)) {
      return;
    }

    if(!required || !installed) {
      return undefined;
    }

    var a = installed.split('.');
    var b = required.split('.');
    var i;

    // leave suffixes as is ("RC1 or -SNAPSHOT")
    for (i = 0; i < Math.min(a.length, 3); ++i) {
      a[i] = Number(a[i]);
    }
    for (i = 0; i < Math.min(b.length, 3); ++i) {
      b[i] = Number(b[i]);
    }
    if (a.length === 2) {
      a[2] = 0;
    }

    if (a[0] > b[0]){return true;}
    if (a[0] < b[0]){return false;}

    if (a[1] > b[1]){return true;}
    if (a[1] < b[1]){return false;}

    if (a[2] > b[2]){return true;}
    if (a[2] < b[2]){return false;}

    if (a.length > 3) {
      // rc/beta suffix
      if (b.length <= 3) {
        return false;
      } // no suffix on b -> a<b
      return a[3] >= b[3];
    }
    if (b.length > 3) {
      // b has a suffix but a not -> a>b
      return true;
    }

    return true;
  };

  this.init();

});

});