import { uniqBy } from 'lodash';

import { Labels } from '@grafana/data';
import { Matcher, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
import { Alert } from 'app/types/unified-alerting';

import { MatcherFieldValue } from '../types/silence-form';

import { parseMatcher } from './alertmanager';

// Parses a list of entries like like "['foo=bar', 'baz=~bad*']" into SilenceMatcher[]
export function parseQueryParamMatchers(matcherPairs: string[]): Matcher[] {
  const parsedMatchers = matcherPairs.filter((x) => !!x.trim()).map((x) => parseMatcher(x.trim()));

  // Due to migration, old alert rules might have a duplicated alertname label
  // To handle that case want to filter out duplicates and make sure there are only unique labels
  return uniqBy(parsedMatchers, (matcher) => matcher.name);
}

export const getMatcherQueryParams = (labels: Labels) => {
  const validMatcherLabels = Object.entries(labels).filter(
    ([labelKey]) => !(labelKey.startsWith('__') && labelKey.endsWith('__'))
  );

  const matcherUrlParams = new URLSearchParams();
  validMatcherLabels.forEach(([labelKey, labelValue]) =>
    matcherUrlParams.append('matcher', `${labelKey}=${labelValue}`)
  );

  return matcherUrlParams;
};

interface MatchedInstance {
  id: string;
  data: {
    matchedInstance: Alert;
  };
}

export const findAlertInstancesWithMatchers = (
  instances: Alert[],
  matchers: MatcherFieldValue[]
): MatchedInstance[] => {
  const anchorRegex = (regexpString: string): RegExp => {
    // Silence matchers are always fully anchored in the Alertmanager: https://github.com/prometheus/alertmanager/pull/748
    if (!regexpString.startsWith('^')) {
      regexpString = '^' + regexpString;
    }
    if (!regexpString.endsWith('$')) {
      regexpString = regexpString + '$';
    }
    return new RegExp(regexpString);
  };

  const matchesInstance = (instance: Alert, matcher: MatcherFieldValue) => {
    return Object.entries(instance.labels).some(([key, value]) => {
      if (!matcher.name || !matcher.value) {
        return false;
      }
      if (matcher.name !== key) {
        return false;
      }
      switch (matcher.operator) {
        case MatcherOperator.equal:
          return matcher.value === value;
        case MatcherOperator.notEqual:
          return matcher.value !== value;
        case MatcherOperator.regex:
          const regex = anchorRegex(matcher.value);
          return regex.test(value);
        case MatcherOperator.notRegex:
          const negregex = anchorRegex(matcher.value);
          return !negregex.test(value);
        default:
          return false;
      }
    });
  };

  const filteredInstances = instances.filter((instance) => {
    return matchers.every((matcher) => matchesInstance(instance, matcher));
  });
  const mappedInstances = filteredInstances.map((instance) => ({
    id: `${instance.activeAt}-${instance.value}`,
    data: { matchedInstance: instance },
  }));

  return mappedInstances;
};
