import _ from 'lodash';
import coreModule from 'app/core/core_module';
import config from 'app/core/config';
import { importPluginModule } from './plugin_loader';

export class DatasourceSrv {
  datasources: any;

  /** @ngInject */
  constructor(private $q, private $injector, $rootScope, private templateSrv) {
    this.init();
  }

  init() {
    this.datasources = {};
  }

  get(name) {
    if (!name) {
      return this.get(config.defaultDatasource);
    }

    name = this.templateSrv.replace(name);

    if (name === 'default') {
      return this.get(config.defaultDatasource);
    }

    if (this.datasources[name]) {
      return this.$q.when(this.datasources[name]);
    }

    return this.loadDatasource(name);
  }

  loadDatasource(name) {
    var dsConfig = config.datasources[name];
    if (!dsConfig) {
      return this.$q.reject({ message: 'Datasource named ' + name + ' was not found' });
    }

    var deferred = this.$q.defer();
    var pluginDef = dsConfig.meta;

    importPluginModule(pluginDef.module)
      .then(plugin => {
        // check if its in cache now
        if (this.datasources[name]) {
          deferred.resolve(this.datasources[name]);
          return;
        }

        // plugin module needs to export a constructor function named Datasource
        if (!plugin.Datasource) {
          throw new Error('Plugin module is missing Datasource constructor');
        }

        var instance = this.$injector.instantiate(plugin.Datasource, { instanceSettings: dsConfig });
        instance.meta = pluginDef;
        instance.name = name;
        this.datasources[name] = instance;
        deferred.resolve(instance);
      })
      .catch(function(err) {
        this.$rootScope.appEvent('alert-error', [dsConfig.name + ' plugin failed', err.toString()]);
      });

    return deferred.promise;
  }

  getAll() {
    return config.datasources;
  }

  getAnnotationSources() {
    var sources = [];

    this.addDataSourceVariables(sources);

    _.each(config.datasources, function(value) {
      if (value.meta && value.meta.annotations) {
        sources.push(value);
      }
    });

    return sources;
  }

  getMetricSources(options) {
    var metricSources = [];

    _.each(config.datasources, function(value, key) {
      if (value.meta && value.meta.metrics) {
        metricSources.push({ value: key, name: key, meta: value.meta });

        if (key === config.defaultDatasource) {
          metricSources.push({ value: null, name: 'default', meta: value.meta });
        }
      }
    });

    if (!options || !options.skipVariables) {
      this.addDataSourceVariables(metricSources);
    }

    metricSources.sort(function(a, b) {
      // these two should always be at the bottom
      if (a.meta.id === 'mixed' || a.meta.id === 'grafana') {
        return 1;
      }
      if (b.meta.id === 'mixed' || b.meta.id === 'grafana') {
        return -1;
      }
      if (a.name.toLowerCase() > b.name.toLowerCase()) {
        return 1;
      }
      if (a.name.toLowerCase() < b.name.toLowerCase()) {
        return -1;
      }
      return 0;
    });

    return metricSources;
  }

  addDataSourceVariables(list) {
    // look for data source variables
    for (var i = 0; i < this.templateSrv.variables.length; i++) {
      var variable = this.templateSrv.variables[i];
      if (variable.type !== 'datasource') {
        continue;
      }

      var first = variable.current.value;
      if (first === 'default') {
        first = config.defaultDatasource;
      }

      var ds = config.datasources[first];

      if (ds) {
        list.push({
          name: '$' + variable.name,
          value: '$' + variable.name,
          meta: ds.meta,
        });
      }
    }
  }
}

coreModule.service('datasourceSrv', DatasourceSrv);
