import _ from 'lodash';
import { QueryHint, QueryFix } from '@grafana/data';

/**
 * Number of time series results needed before starting to suggest sum aggregation hints
 */
export const SUM_HINT_THRESHOLD_COUNT = 20;

export function getQueryHints(query: string, series?: any[], datasource?: any): QueryHint[] | null {
  const hints = [];

  // ..._bucket metric needs a histogram_quantile()
  const histogramMetric = query.trim().match(/^\w+_bucket$/);
  if (histogramMetric) {
    const label = 'Time series has buckets, you probably wanted a histogram.';
    hints.push({
      type: 'HISTOGRAM_QUANTILE',
      label,
      fix: {
        label: 'Fix by adding histogram_quantile().',
        action: {
          type: 'ADD_HISTOGRAM_QUANTILE',
          query,
        },
      } as QueryFix,
    });
  }

  // Check for monotonicity on series (table results are being ignored here)
  if (series && series.length > 0) {
    series.forEach(s => {
      const datapoints: number[][] = s.datapoints || s.rows || [];
      if (query.indexOf('rate(') === -1 && datapoints.length > 1) {
        let increasing = false;
        const nonNullData = datapoints.filter(dp => dp[0] !== null);
        const monotonic = nonNullData.every((dp, index) => {
          if (index === 0) {
            return true;
          }
          increasing = increasing || dp[0] > nonNullData[index - 1][0];
          // monotonic?
          return dp[0] >= nonNullData[index - 1][0];
        });
        if (increasing && monotonic) {
          const simpleMetric = query.trim().match(/^\w+$/);
          let label = 'Time series is monotonically increasing.';
          let fix: QueryFix;
          if (simpleMetric) {
            fix = {
              label: 'Fix by adding rate().',
              action: {
                type: 'ADD_RATE',
                query,
              },
            } as QueryFix;
          } else {
            label = `${label} Try applying a rate() function.`;
          }
          hints.push({
            type: 'APPLY_RATE',
            label,
            fix,
          });
        }
      }
    });
  }

  // Check for recording rules expansion
  if (datasource && datasource.ruleMappings) {
    const mapping = datasource.ruleMappings;
    const mappingForQuery = Object.keys(mapping).reduce((acc, ruleName) => {
      if (query.search(ruleName) > -1) {
        return {
          ...acc,
          [ruleName]: mapping[ruleName],
        };
      }
      return acc;
    }, {});
    if (_.size(mappingForQuery) > 0) {
      const label = 'Query contains recording rules.';
      hints.push({
        type: 'EXPAND_RULES',
        label,
        fix: ({
          label: 'Expand rules',
          action: {
            type: 'EXPAND_RULES',
            query,
            mapping: mappingForQuery,
          },
        } as any) as QueryFix,
      });
    }
  }

  if (series && series.length >= SUM_HINT_THRESHOLD_COUNT) {
    const simpleMetric = query.trim().match(/^\w+$/);
    if (simpleMetric) {
      hints.push({
        type: 'ADD_SUM',
        label: 'Many time series results returned.',
        fix: {
          label: 'Consider aggregating with sum().',
          action: {
            type: 'ADD_SUM',
            query: query,
            preventSubmit: true,
          },
        } as QueryFix,
      });
    }
  }

  return hints.length > 0 ? hints : null;
}
