import { PrometheusDatasource } from '../datasource';
import PrometheusMetricFindQuery from '../metric_find_query';
//@ts-ignore
import q from 'q';
import { toUtc } from '@grafana/data';
import { DataSourceInstanceSettings } from '@grafana/ui';
import { PromOptions } from '../types';

describe('PrometheusMetricFindQuery', () => {
  const instanceSettings = ({
    url: 'proxied',
    directUrl: 'direct',
    user: 'test',
    password: 'mupp',
    jsonData: { httpMethod: 'GET' },
  } as unknown) as DataSourceInstanceSettings<PromOptions>;
  const raw = {
    from: toUtc('2018-04-25 10:00'),
    to: toUtc('2018-04-25 11:00'),
  };
  const ctx: any = {
    backendSrvMock: {
      datasourceRequest: jest.fn(() => Promise.resolve({})),
    },
    templateSrvMock: {
      replace: (a: string) => a,
    },
    timeSrvMock: {
      timeRange: () => ({
        from: raw.from,
        to: raw.to,
        raw: raw,
      }),
    },
  };

  ctx.setupMetricFindQuery = (data: any) => {
    ctx.backendSrvMock.datasourceRequest.mockReturnValue(Promise.resolve({ status: 'success', data: data.response }));
    return new PrometheusMetricFindQuery(ctx.ds, data.query, ctx.timeSrvMock);
  };

  beforeEach(() => {
    ctx.backendSrvMock.datasourceRequest.mockReset();
    ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
  });

  describe('When performing metricFindQuery', () => {
    it('label_names() should generate label name search query', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'label_names()',
        response: {
          data: ['name1', 'name2', 'name3'],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: 'proxied/api/v1/labels',
        silent: true,
        headers: {},
      });
    });

    it('label_values(resource) should generate label search query', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'label_values(resource)',
        response: {
          data: ['value1', 'value2', 'value3'],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: 'proxied/api/v1/label/resource/values',
        silent: true,
        headers: {},
      });
    });

    it('label_values(metric, resource) should generate series query with correct time', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'label_values(metric, resource)',
        response: {
          data: [
            { __name__: 'metric', resource: 'value1' },
            { __name__: 'metric', resource: 'value2' },
            { __name__: 'metric', resource: 'value3' },
          ],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`,
        silent: true,
        headers: {},
      });
    });

    it('label_values(metric{label1="foo", label2="bar", label3="baz"}, resource) should generate series query with correct time', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'label_values(metric{label1="foo", label2="bar", label3="baz"}, resource)',
        response: {
          data: [
            { __name__: 'metric', resource: 'value1' },
            { __name__: 'metric', resource: 'value2' },
            { __name__: 'metric', resource: 'value3' },
          ],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: `proxied/api/v1/series?match[]=${encodeURIComponent(
          'metric{label1="foo", label2="bar", label3="baz"}'
        )}&start=${raw.from.unix()}&end=${raw.to.unix()}`,
        silent: true,
        headers: {},
      });
    });

    it('label_values(metric, resource) result should not contain empty string', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'label_values(metric, resource)',
        response: {
          data: [
            { __name__: 'metric', resource: 'value1' },
            { __name__: 'metric', resource: 'value2' },
            { __name__: 'metric', resource: '' },
          ],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(2);
      expect(results[0].text).toBe('value1');
      expect(results[1].text).toBe('value2');
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`,
        silent: true,
        headers: {},
      });
    });

    it('metrics(metric.*) should generate metric name query', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'metrics(metric.*)',
        response: {
          data: ['metric1', 'metric2', 'metric3', 'nomatch'],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: 'proxied/api/v1/label/__name__/values',
        silent: true,
        headers: {},
      });
    });

    it('query_result(metric) should generate metric name query', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'query_result(metric)',
        response: {
          data: {
            resultType: 'vector',
            result: [
              {
                metric: { __name__: 'metric', job: 'testjob' },
                value: [1443454528.0, '3846'],
              },
            ],
          },
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(1);
      expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000');
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`,
        requestId: undefined,
        headers: {},
      });
    });

    it('up{job="job1"} should fallback using generate series query', async () => {
      const query = ctx.setupMetricFindQuery({
        query: 'up{job="job1"}',
        response: {
          data: [
            { __name__: 'up', instance: '127.0.0.1:1234', job: 'job1' },
            { __name__: 'up', instance: '127.0.0.1:5678', job: 'job1' },
            { __name__: 'up', instance: '127.0.0.1:9102', job: 'job1' },
          ],
        },
      });
      const results = await query.process();

      expect(results).toHaveLength(3);
      expect(results[0].text).toBe('up{instance="127.0.0.1:1234",job="job1"}');
      expect(results[1].text).toBe('up{instance="127.0.0.1:5678",job="job1"}');
      expect(results[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}');
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
      expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
        method: 'GET',
        url: `proxied/api/v1/series?match[]=${encodeURIComponent(
          'up{job="job1"}'
        )}&start=${raw.from.unix()}&end=${raw.to.unix()}`,
        silent: true,
        headers: {},
      });
    });
  });
});
