import * as monitoringUtils from '~/monitoring/utils';
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
import {
  graphDataPrometheusQuery,
  graphDataPrometheusQueryRange,
  anomalyMockGraphData,
} from './mock_data';

describe('monitoring/utils', () => {
  const generatedLink = 'http://chart.link.com';
  const chartTitle = 'Some metric chart';

  describe('trackGenerateLinkToChartEventOptions', () => {
    it('should return Cluster Monitoring options if located on Cluster Health Dashboard', () => {
      document.body.dataset.page = 'groups:clusters:show';

      expect(monitoringUtils.generateLinkToChartOptions(generatedLink)).toEqual({
        category: 'Cluster Monitoring',
        action: 'generate_link_to_cluster_metric_chart',
        label: 'Chart link',
        property: generatedLink,
      });
    });

    it('should return Incident Management event options if located on Metrics Dashboard', () => {
      document.body.dataset.page = 'metrics:show';

      expect(monitoringUtils.generateLinkToChartOptions(generatedLink)).toEqual({
        category: 'Incident Management::Embedded metrics',
        action: 'generate_link_to_metrics_chart',
        label: 'Chart link',
        property: generatedLink,
      });
    });
  });

  describe('trackDownloadCSVEvent', () => {
    it('should return Cluster Monitoring options if located on Cluster Health Dashboard', () => {
      document.body.dataset.page = 'groups:clusters:show';

      expect(monitoringUtils.downloadCSVOptions(chartTitle)).toEqual({
        category: 'Cluster Monitoring',
        action: 'download_csv_of_cluster_metric_chart',
        label: 'Chart title',
        property: chartTitle,
      });
    });

    it('should return Incident Management event options if located on Metrics Dashboard', () => {
      document.body.dataset.page = 'metriss:show';

      expect(monitoringUtils.downloadCSVOptions(chartTitle)).toEqual({
        category: 'Incident Management::Embedded metrics',
        action: 'download_csv_of_metrics_dashboard_chart',
        label: 'Chart title',
        property: chartTitle,
      });
    });
  });

  describe('getTimeDiff', () => {
    function secondsBetween({ start, end }) {
      return (new Date(end) - new Date(start)) / 1000;
    }

    function minutesBetween(timeRange) {
      return secondsBetween(timeRange) / 60;
    }

    function hoursBetween(timeRange) {
      return minutesBetween(timeRange) / 60;
    }

    it('defaults to an 8 hour (28800s) difference', () => {
      const params = monitoringUtils.getTimeDiff();

      expect(hoursBetween(params)).toEqual(8);
    });

    it('accepts time window as an argument', () => {
      const params = monitoringUtils.getTimeDiff('thirtyMinutes');

      expect(minutesBetween(params)).toEqual(30);
    });

    it('returns a value for every defined time window', () => {
      const nonDefaultWindows = Object.keys(timeWindows).filter(window => window !== 'eightHours');

      nonDefaultWindows.forEach(timeWindow => {
        const params = monitoringUtils.getTimeDiff(timeWindow);

        // Ensure we're not returning the default
        expect(hoursBetween(params)).not.toEqual(8);
      });
    });
  });

  describe('getTimeWindow', () => {
    [
      {
        args: [
          {
            start: '2019-10-01T18:27:47.000Z',
            end: '2019-10-01T21:27:47.000Z',
          },
        ],
        expected: timeWindowsKeyNames.threeHours,
      },
      {
        args: [
          {
            start: '2019-10-01T28:27:47.000Z',
            end: '2019-10-01T21:27:47.000Z',
          },
        ],
        expected: null,
      },
      {
        args: [
          {
            start: '',
            end: '',
          },
        ],
        expected: null,
      },
      {
        args: [
          {
            start: null,
            end: null,
          },
        ],
        expected: null,
      },
      {
        args: [{}],
        expected: null,
      },
    ].forEach(({ args, expected }) => {
      it(`returns "${expected}" with args=${JSON.stringify(args)}`, () => {
        expect(monitoringUtils.getTimeWindow(...args)).toEqual(expected);
      });
    });
  });

  describe('graphDataValidatorForValues', () => {
    /*
     * When dealing with a metric using the query format, e.g.
     * query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024'
     * the validator will look for the `value` key instead of `values`
     */
    it('validates data with the query format', () => {
      const validGraphData = monitoringUtils.graphDataValidatorForValues(
        true,
        graphDataPrometheusQuery,
      );

      expect(validGraphData).toBe(true);
    });

    /*
     * When dealing with a metric using the query?range format, e.g.
     * query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job)  /1024/1024/1024',
     * the validator will look for the `values` key instead of `value`
     */
    it('validates data with the query_range format', () => {
      const validGraphData = monitoringUtils.graphDataValidatorForValues(
        false,
        graphDataPrometheusQueryRange,
      );

      expect(validGraphData).toBe(true);
    });
  });

  describe('stringToISODate', () => {
    ['', 'null', undefined, 'abc'].forEach(input => {
      it(`throws error for invalid input like ${input}`, done => {
        try {
          monitoringUtils.stringToISODate(input);
        } catch (e) {
          expect(e).toBeDefined();
          done();
        }
      });
    });
    [
      {
        input: '2019-09-09 01:01:01',
        output: '2019-09-09T01:01:01Z',
      },
      {
        input: '2019-09-09 00:00:00',
        output: '2019-09-09T00:00:00Z',
      },
      {
        input: '2019-09-09 23:59:59',
        output: '2019-09-09T23:59:59Z',
      },
      {
        input: '2019-09-09',
        output: '2019-09-09T00:00:00Z',
      },
    ].forEach(({ input, output }) => {
      it(`returns ${output} from ${input}`, () => {
        expect(monitoringUtils.stringToISODate(input)).toBe(output);
      });
    });
  });

  describe('ISODateToString', () => {
    [
      {
        input: new Date('2019-09-09T00:00:00.000Z'),
        output: '2019-09-09 00:00:00',
      },
      {
        input: new Date('2019-09-09T07:00:00.000Z'),
        output: '2019-09-09 07:00:00',
      },
    ].forEach(({ input, output }) => {
      it(`ISODateToString return ${output} for ${input}`, () => {
        expect(monitoringUtils.ISODateToString(input)).toBe(output);
      });
    });
  });

  describe('truncateZerosInDateTime', () => {
    [
      {
        input: '',
        output: '',
      },
      {
        input: '2019-10-10',
        output: '2019-10-10',
      },
      {
        input: '2019-10-10 00:00:01',
        output: '2019-10-10 00:00:01',
      },
      {
        input: '2019-10-10 00:00:00',
        output: '2019-10-10',
      },
    ].forEach(({ input, output }) => {
      it(`truncateZerosInDateTime return ${output} for ${input}`, () => {
        expect(monitoringUtils.truncateZerosInDateTime(input)).toBe(output);
      });
    });
  });

  describe('isValidDate', () => {
    [
      {
        input: '2019-09-09T00:00:00.000Z',
        output: true,
      },
      {
        input: '2019-09-09T000:00.000Z',
        output: false,
      },
      {
        input: 'a2019-09-09T000:00.000Z',
        output: false,
      },
      {
        input: '2019-09-09T',
        output: false,
      },
      {
        input: '2019-09-09',
        output: true,
      },
      {
        input: '2019-9-9',
        output: true,
      },
      {
        input: '2019-9-',
        output: true,
      },
      {
        input: '2019--',
        output: false,
      },
      {
        input: '2019',
        output: true,
      },
      {
        input: '',
        output: false,
      },
      {
        input: null,
        output: false,
      },
    ].forEach(({ input, output }) => {
      it(`isValidDate return ${output} for ${input}`, () => {
        expect(monitoringUtils.isValidDate(input)).toBe(output);
      });
    });
  });

  describe('isDateTimePickerInputValid', () => {
    [
      {
        input: null,
        output: false,
      },
      {
        input: '',
        output: false,
      },
      {
        input: 'xxxx-xx-xx',
        output: false,
      },
      {
        input: '9999-99-19',
        output: false,
      },
      {
        input: '2019-19-23',
        output: false,
      },
      {
        input: '2019-09-23',
        output: true,
      },
      {
        input: '2019-09-23 x',
        output: false,
      },
      {
        input: '2019-09-29 0:0:0',
        output: false,
      },
      {
        input: '2019-09-29 00:00:00',
        output: true,
      },
      {
        input: '2019-09-29 24:24:24',
        output: false,
      },
      {
        input: '2019-09-29 23:24:24',
        output: true,
      },
      {
        input: '2019-09-29 23:24:24 ',
        output: false,
      },
    ].forEach(({ input, output }) => {
      it(`returns ${output} for ${input}`, () => {
        expect(monitoringUtils.isDateTimePickerInputValid(input)).toBe(output);
      });
    });
  });

  describe('graphDataValidatorForAnomalyValues', () => {
    let oneMetric;
    let threeMetrics;
    let fourMetrics;
    beforeEach(() => {
      oneMetric = graphDataPrometheusQuery;
      threeMetrics = anomalyMockGraphData;

      const metrics = [...threeMetrics.metrics];
      metrics.push(threeMetrics.metrics[0]);
      fourMetrics = {
        ...anomalyMockGraphData,
        metrics,
      };
    });
    /*
     * Anomaly charts can accept results for exactly 3 metrics,
     */
    it('validates passes with the right query format', () => {
      expect(monitoringUtils.graphDataValidatorForAnomalyValues(threeMetrics)).toBe(true);
    });

    it('validation fails for wrong format, 1 metric', () => {
      expect(monitoringUtils.graphDataValidatorForAnomalyValues(oneMetric)).toBe(false);
    });

    it('validation fails for wrong format, more than 3 metrics', () => {
      expect(monitoringUtils.graphDataValidatorForAnomalyValues(fourMetrics)).toBe(false);
    });
  });
});