import { PromAlertingRuleState, PromRuleType } from '../../../../types/unified-alerting-dto';
import { getFilter } from '../utils/search';

import { RuleHealth, applySearchFilterToQuery, getSearchFilterFromQuery } from './rulesSearchParser';

describe('Alert rules searchParser', () => {
  describe('getSearchFilterFromQuery', () => {
    it.each(['datasource:prometheus'])('should parse data source filter from "%s" query', (query) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.dataSourceNames).toEqual(['prometheus']);
    });

    it.each(['namespace:integrations-node'])('should parse namespace filter from "%s" query', (query) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.namespace).toBe('integrations-node');
    });

    it.each(['label:team label:region=emea'])('should parse label filter from "%s" query', (query) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.labels).toHaveLength(2);
      expect(filter.labels).toContain('team');
      expect(filter.labels).toContain('region=emea');
    });

    it.each(['group:cpu-utilization'])('should parse group filter from "%s" query', (query) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.groupName).toBe('cpu-utilization');
    });

    it.each(['rule:cpu-80%-alert'])('should parse rule name filter from "%s" query', (query) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.ruleName).toBe('cpu-80%-alert');
    });

    it.each([
      { query: 'state:firing', expectedFilter: PromAlertingRuleState.Firing },
      { query: 'state:inactive', expectedFilter: PromAlertingRuleState.Inactive },
      { query: 'state:normal', expectedFilter: PromAlertingRuleState.Inactive },
      { query: 'state:pending', expectedFilter: PromAlertingRuleState.Pending },
    ])('should parse $expectedFilter rule state filter from "$query" query', ({ query, expectedFilter }) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.ruleState).toBe(expectedFilter);
    });

    it.each([
      { query: 'type:alerting', expectedFilter: PromRuleType.Alerting },
      { query: 'type:recording', expectedFilter: PromRuleType.Recording },
    ])('should parse $expectedFilter rule type filter from "$query" input', ({ query, expectedFilter }) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.ruleType).toBe(expectedFilter);
    });

    it.each([
      { query: 'health:ok', expectedFilter: RuleHealth.Ok },
      { query: 'health:nodata', expectedFilter: RuleHealth.NoData },
      { query: 'health:error', expectedFilter: RuleHealth.Error },
    ])('should parse RuleHealth $expectedFilter filter from "$query" query', ({ query, expectedFilter }) => {
      const filter = getSearchFilterFromQuery(query);
      expect(filter.ruleHealth).toBe(expectedFilter);
    });

    it.each([{ query: 'contactPoint:slack', expectedFilter: 'slack' }])(
      `should parse contactPoint $expectedFilter filter from "$query" query`,
      ({ query, expectedFilter }) => {
        const filter = getSearchFilterFromQuery(query);
        expect(filter.contactPoint).toBe(expectedFilter);
      }
    );

    it.each([{ query: 'policy:team-a-policy', expectedFilter: 'team-a-policy' }])(
      `should parse policy $expectedFilter filter from "$query" query`,
      ({ query, expectedFilter }) => {
        const filter = getSearchFilterFromQuery(query);
        expect(filter.policy).toBe(expectedFilter);
      }
    );

    it('should parse non-filtering words as free form query', () => {
      const filter = getSearchFilterFromQuery('cpu usage rule');
      expect(filter.freeFormWords).toHaveLength(3);
      expect(filter.freeFormWords).toContain('cpu');
      expect(filter.freeFormWords).toContain('usage');
      expect(filter.freeFormWords).toContain('rule');
    });

    it('should parse free words with quotes', () => {
      const query = '"hello world" hello world';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.freeFormWords).toEqual(['hello world', 'hello', 'world']);
    });

    it('should parse filter values with whitespaces when in quotes', () => {
      const query =
        'datasource:"prom dev" namespace:"node one" label:"team=frontend us" group:"cpu alerts" rule:"cpu failure"';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.dataSourceNames).toEqual(['prom dev']);
      expect(filter.namespace).toBe('node one');
      expect(filter.labels).toContain('team=frontend us');
      expect(filter.groupName).toContain('cpu alerts');
      expect(filter.ruleName).toContain('cpu failure');
    });

    it('should parse filter values with special characters', () => {
      const query =
        'datasource:prom::dev/linux>>; namespace:"[{node}] (#20+)" label:_region=apac|emea\\nasa group:$20.00%$ rule:"cpu!! & memory.,?"';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.dataSourceNames).toEqual(['prom::dev/linux>>;']);
      expect(filter.namespace).toBe('[{node}] (#20+)');
      expect(filter.labels).toContain('_region=apac|emea\\nasa');
      expect(filter.groupName).toContain('$20.00%$');
      expect(filter.ruleName).toContain('cpu!! & memory.,?');
    });

    it('should parse non-filter terms with colon as free form words', () => {
      const query = 'cpu:high-utilization memory:overload';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.freeFormWords).toContain('cpu:high-utilization');
      expect(filter.freeFormWords).toContain('memory:overload');
    });

    it('should parse mixed free form words and filters', () => {
      const query = 'datasource:prometheus utilization label:team cpu';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.dataSourceNames).toEqual(['prometheus']);
      expect(filter.labels).toContain('team');
      expect(filter.freeFormWords).toContain('utilization');
      expect(filter.freeFormWords).toContain('cpu');
    });

    it('should parse labels containing matchers', () => {
      const query = 'label:region!=US label:"team=~fe.*devs" label:cluster!~ba.+';
      const filter = getSearchFilterFromQuery(query);

      expect(filter.labels).toContain('region!=US');
      expect(filter.labels).toContain('team=~fe.*devs');
      expect(filter.labels).toContain('cluster!~ba.+');
    });

    it('should discard contactPoint when both contactPoint and policy are present in query', () => {
      const filter = getSearchFilterFromQuery('contactPoint:slack policy:team-a-policy');
      expect(filter.policy).toBe('team-a-policy');
      expect(filter.contactPoint).toBeUndefined();
    });
  });

  describe('applySearchFilterToQuery', () => {
    it('should apply policy filter to query', () => {
      const filter = getFilter({ policy: 'team-a-policy' });
      const query = applySearchFilterToQuery('', filter);
      expect(query).toContain('policy:team-a-policy');
    });

    it('should apply filters to an empty query', () => {
      const filter = getFilter({
        freeFormWords: ['cpu', 'eighty'],
        dataSourceNames: ['Mimir Dev'],
        namespace: '/etc/prometheus',
        labels: ['team', 'region=apac'],
        groupName: 'cpu-usage',
        ruleName: 'cpu > 80%',
        ruleType: PromRuleType.Alerting,
        ruleState: PromAlertingRuleState.Firing,
        ruleHealth: RuleHealth.Ok,
        contactPoint: 'slack',
      });

      const query = applySearchFilterToQuery('', filter);

      expect(query).toBe(
        'datasource:"Mimir Dev" namespace:/etc/prometheus group:cpu-usage rule:"cpu > 80%" state:firing type:alerting health:ok label:team label:region=apac cpu eighty contactPoint:slack'
      );
    });

    it('should update filters in existing query', () => {
      const filter = getFilter({
        dataSourceNames: ['Mimir Dev'],
        namespace: '/etc/prometheus',
        labels: ['team', 'region=apac'],
        groupName: 'cpu-usage',
        ruleName: 'cpu > 80%',
        contactPoint: 'slack',
      });

      const baseQuery = 'datasource:prometheus namespace:mimir-global group:memory rule:"mem > 90% label:severity"';
      const query = applySearchFilterToQuery(baseQuery, filter);

      expect(query).toBe(
        'datasource:"Mimir Dev" namespace:/etc/prometheus group:cpu-usage rule:"cpu > 80%" label:team label:region=apac contactPoint:slack'
      );
    });

    it('should preserve the order of parameters when updating', () => {
      const filter = getFilter({
        dataSourceNames: ['Mimir Dev'],
        namespace: '/etc/prometheus',
        labels: ['region=emea'],
        groupName: 'cpu-usage',
        contactPoint: 'cp3',
        ruleName: 'cpu > 80%',
      });

      const baseQuery =
        'label:region=apac rule:"mem > 90%" group:memory contactPoint:cp4 namespace:mimir-global datasource:prometheus';
      const query = applySearchFilterToQuery(baseQuery, filter);

      expect(query).toBe(
        'label:region=emea rule:"cpu > 80%" group:cpu-usage contactPoint:cp3 namespace:/etc/prometheus datasource:"Mimir Dev"'
      );
    });

    it('should not serialize contactPoint when policy is also present', () => {
      const filter = getFilter({ policy: 'team-a-policy' });
      const query = applySearchFilterToQuery('', filter);
      expect(query).toContain('policy:team-a-policy');
      expect(query).not.toContain('contactPoint');
    });
  });
});
