import _ from 'lodash';
import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common';
import moment from 'moment';
import angular from 'angular';
import helpers from 'test/specs/helpers';
import { ElasticDatasource } from '../datasource';

describe('ElasticDatasource', function() {
  var ctx = new helpers.ServiceTestContext();

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.services'));
  beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv']));

  beforeEach(
    angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
      ctx.$q = $q;
      ctx.$httpBackend = $httpBackend;
      ctx.$rootScope = $rootScope;
      ctx.$injector = $injector;
      $httpBackend.when('GET', /\.html$/).respond('');
    })
  );

  function createDatasource(instanceSettings) {
    instanceSettings.jsonData = instanceSettings.jsonData || {};
    ctx.ds = ctx.$injector.instantiate(ElasticDatasource, {
      instanceSettings: instanceSettings,
    });
  }

  describe('When testing datasource with index pattern', function() {
    beforeEach(function() {
      createDatasource({
        url: 'http://es.com',
        index: '[asd-]YYYY.MM.DD',
        jsonData: { interval: 'Daily', esVersion: '2' },
      });
    });

    it('should translate index pattern to current day', function() {
      var requestOptions;
      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({ data: {} });
      };

      ctx.ds.testDatasource();
      ctx.$rootScope.$apply();

      var today = moment.utc().format('YYYY.MM.DD');
      expect(requestOptions.url).to.be('http://es.com/asd-' + today + '/_mapping');
    });
  });

  describe('When issuing metric query with interval pattern', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({
        url: 'http://es.com',
        index: '[asd-]YYYY.MM.DD',
        jsonData: { interval: 'Daily', esVersion: '2' },
      });

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({ data: { responses: [] } });
      };

      ctx.ds.query({
        range: {
          from: moment.utc([2015, 4, 30, 10]),
          to: moment.utc([2015, 5, 1, 10]),
        },
        targets: [
          {
            bucketAggs: [],
            metrics: [{ type: 'raw_document' }],
            query: 'escape\\:test',
          },
        ],
      });

      ctx.$rootScope.$apply();

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should translate index pattern to current day', function() {
      expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']);
    });

    it('should json escape lucene query', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.query.bool.filter[1].query_string.query).to.be('escape\\:test');
    });
  });

  describe('When issuing document query', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({
        url: 'http://es.com',
        index: 'test',
        jsonData: { esVersion: '2' },
      });

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({ data: { responses: [] } });
      };

      ctx.ds.query({
        range: {
          from: moment([2015, 4, 30, 10]),
          to: moment([2015, 5, 1, 10]),
        },
        targets: [
          {
            bucketAggs: [],
            metrics: [{ type: 'raw_document' }],
            query: 'test',
          },
        ],
      });

      ctx.$rootScope.$apply();
      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should set search type to query_then_fetch', function() {
      expect(header.search_type).to.eql('query_then_fetch');
    });

    it('should set size', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.size).to.be(500);
    });
  });

  describe('When getting fields', function() {
    beforeEach(function() {
      createDatasource({ url: 'http://es.com', index: 'metricbeat' });

      ctx.backendSrv.datasourceRequest = function(options) {
        return ctx.$q.when({
          data: {
            metricbeat: {
              mappings: {
                metricsets: {
                  _all: {},
                  properties: {
                    '@timestamp': { type: 'date' },
                    beat: {
                      properties: {
                        name: {
                          fields: { raw: { type: 'keyword' } },
                          type: 'string',
                        },
                        hostname: { type: 'string' },
                      },
                    },
                    system: {
                      properties: {
                        cpu: {
                          properties: {
                            system: { type: 'float' },
                            user: { type: 'float' },
                          },
                        },
                        process: {
                          properties: {
                            cpu: {
                              properties: {
                                total: { type: 'float' },
                              },
                            },
                            name: { type: 'string' },
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        });
      };
    });

    it('should return nested fields', function() {
      ctx.ds
        .getFields({
          find: 'fields',
          query: '*',
        })
        .then(fieldObjects => {
          var fields = _.map(fieldObjects, 'text');
          expect(fields).to.eql([
            '@timestamp',
            'beat.name.raw',
            'beat.name',
            'beat.hostname',
            'system.cpu.system',
            'system.cpu.user',
            'system.process.cpu.total',
            'system.process.name',
          ]);
        });
      ctx.$rootScope.$apply();
    });

    it('should return fields related to query type', function() {
      ctx.ds
        .getFields({
          find: 'fields',
          query: '*',
          type: 'number',
        })
        .then(fieldObjects => {
          var fields = _.map(fieldObjects, 'text');
          expect(fields).to.eql(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']);
        });

      ctx.ds
        .getFields({
          find: 'fields',
          query: '*',
          type: 'date',
        })
        .then(fieldObjects => {
          var fields = _.map(fieldObjects, 'text');
          expect(fields).to.eql(['@timestamp']);
        });

      ctx.$rootScope.$apply();
    });
  });

  describe('When issuing aggregation query on es5.x', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({
        url: 'http://es.com',
        index: 'test',
        jsonData: { esVersion: '5' },
      });

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({ data: { responses: [] } });
      };

      ctx.ds.query({
        range: {
          from: moment([2015, 4, 30, 10]),
          to: moment([2015, 5, 1, 10]),
        },
        targets: [
          {
            bucketAggs: [{ type: 'date_histogram', field: '@timestamp', id: '2' }],
            metrics: [{ type: 'count' }],
            query: 'test',
          },
        ],
      });

      ctx.$rootScope.$apply();
      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should not set search type to count', function() {
      expect(header.search_type).to.not.eql('count');
    });

    it('should set size to 0', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.size).to.be(0);
    });
  });

  describe('When issuing metricFind query on es5.x', function() {
    var requestOptions, parts, header, body, results;

    beforeEach(function() {
      createDatasource({
        url: 'http://es.com',
        index: 'test',
        jsonData: { esVersion: '5' },
      });

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({
          data: {
            responses: [
              {
                aggregations: {
                  '1': {
                    buckets: [
                      { doc_count: 1, key: 'test' },
                      {
                        doc_count: 2,
                        key: 'test2',
                        key_as_string: 'test2_as_string',
                      },
                    ],
                  },
                },
              },
            ],
          },
        });
      };

      ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}').then(res => {
        results = res;
      });

      ctx.$rootScope.$apply();

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
      body = angular.fromJson(parts[1]);
    });

    it('should get results', function() {
      expect(results.length).to.eql(2);
    });

    it('should use key or key_as_string', function() {
      expect(results[0].text).to.eql('test');
      expect(results[1].text).to.eql('test2_as_string');
    });

    it('should not set search type to count', function() {
      expect(header.search_type).to.not.eql('count');
    });

    it('should set size to 0', function() {
      expect(body.size).to.be(0);
    });

    it('should not set terms aggregation size to 0', function() {
      expect(body['aggs']['1']['terms'].size).to.not.be(0);
    });
  });
});
