import { NotifierDTO } from 'app/types';

import { GrafanaManagedContactPoint, Receiver } from '../../../../plugins/datasource/alertmanager/types';
import { grafanaAlertNotifiers, grafanaAlertNotifiersMock } from '../mockGrafanaNotifiers';
import { CloudChannelValues, GrafanaChannelValues, ReceiverFormValues } from '../types/receiver-form';

import {
  convertJiraFieldToJson,
  convertJsonToJiraField,
  formValuesToCloudReceiver,
  formValuesToGrafanaReceiver,
  grafanaReceiverToFormValues,
  omitEmptyUnlessExisting,
  omitEmptyValues,
  omitTemporaryIdentifiers,
} from './receiver-form';

describe('Receiver form utils', () => {
  describe('omitEmptyStringValues', () => {
    it('should recursively omit empty strings but leave other properties in palce', () => {
      const original = {
        one: 'two',
        remove: '',
        three: 0,
        four: null,
        five: [
          [
            {
              foo: 'bar',
              remove: '',
              notDefined: undefined,
            },
          ],
          {
            foo: 'bar',
            remove: '',
          },
        ],
      };

      const expected = {
        one: 'two',
        three: 0,
        five: [
          [
            {
              foo: 'bar',
            },
          ],
          {
            foo: 'bar',
          },
        ],
      };

      expect(omitEmptyValues(original)).toEqual(expected);
    });
  });
  describe('omitEmptyUnlessExisting', () => {
    it('should omit empty strings if no entry in existing', () => {
      const existing = {
        five_keep: true,
      };
      const original = {
        one: 'two',
        two_remove: '',
        three: 0,
        four_remove: null,
        five_keep: '',
      };

      const expected = {
        one: 'two',
        three: 0,
        five_keep: '',
      };

      expect(omitEmptyUnlessExisting(original, existing)).toEqual(expected);
    });
  });

  describe('omitTemporaryIdentifiers', () => {
    it('should remove __id from the root object', () => {
      const original = {
        __id: '1',
        foo: 'bar',
      };

      const expected = {
        foo: 'bar',
      };

      expect(omitTemporaryIdentifiers(original)).toEqual(expected);
    });

    it('should remove __id from nested objects', () => {
      const original = {
        foo: 'bar',
        nested: {
          __id: '1',
          baz: 'qux',
          doubleNested: { __id: '2', url: 'example.com' },
        },
      };

      const expected = {
        foo: 'bar',
        nested: {
          baz: 'qux',
          doubleNested: { url: 'example.com' },
        },
      };

      expect(omitTemporaryIdentifiers(original)).toEqual(expected);
    });

    it('should remove __id from objects in an array', () => {
      const original = {
        foo: 'bar',
        array: [
          {
            __id: '1',
            baz: 'qux',
            actions: [
              { __id: '3', type: 'email' },
              { __id: '4', type: 'slack' },
            ],
          },
          { __id: '2', quux: 'quuz' },
        ],
      };

      const expected = {
        foo: 'bar',
        array: [
          {
            baz: 'qux',
            actions: [{ type: 'email' }, { type: 'slack' }],
          },
          {
            quux: 'quuz',
          },
        ],
      };

      expect(omitTemporaryIdentifiers(original)).toEqual(expected);
    });

    it('should return a new object and keep the original intact', () => {
      const original = {
        foo: 'bar',
        nested: {
          __id: '1',
          baz: 'qux',
          doubleNested: { __id: '2', url: 'example.com' },
        },
      };

      const withOmitted = omitTemporaryIdentifiers(original);

      expect(withOmitted).not.toBe(original);
      expect(original.nested.__id).toBe('1');
      expect(original.nested.doubleNested.__id).toBe('2');
    });
  });
});

describe('formValuesToGrafanaReceiver', () => {
  it('should migrate regular settings to secure settings if the field is defined as secure', () => {
    const formValues: ReceiverFormValues<GrafanaChannelValues> = {
      name: 'my-receiver',
      items: [
        {
          __id: '1',
          secureSettings: {},
          secureFields: {},
          type: 'discord',
          settings: {
            url: 'https://foo.bar/',
          },
          disableResolveMessage: false,
        },
      ],
    };

    const channelMap = {
      '1': {
        uid: 'abc123',
        secureSettings: {},
        secureFields: {},
        type: 'discord',
        settings: {
          url: 'https://foo.bar/',
        },
        disableResolveMessage: false,
      },
    };

    const notifiers = [
      {
        type: 'discord',
        options: [{ propertyName: 'url', secure: true }],
      },
    ] as NotifierDTO[];

    // @ts-expect-error
    expect(formValuesToGrafanaReceiver(formValues, channelMap, {}, notifiers)).toMatchSnapshot();
  });
});

describe('formValuesToCloudReceiver', () => {
  it('should remove temporary ids from receivers and settings', () => {
    const formValues: ReceiverFormValues<CloudChannelValues> = {
      name: 'my-receiver',
      items: [
        {
          __id: '1',
          type: 'slack',
          settings: {
            url: 'https://slack.example.com/',
            actions: [{ __id: '2', text: 'Acknowledge', type: 'button' }],
            fields: [{ __id: '10', title: 'priority', value: '1' }],
          },
          secureFields: {},
          secureSettings: {},
          sendResolved: true,
        },
      ],
    };

    const defaults: CloudChannelValues = {
      __id: '1',
      type: 'slack',
      settings: {
        url: 'https://slack.example.com/',
      },
      secureFields: {},
      secureSettings: {},
      sendResolved: true,
    };

    const expected: Receiver = {
      name: 'my-receiver',
      slack_configs: [
        {
          url: 'https://slack.example.com/',
          actions: [{ text: 'Acknowledge', type: 'button' }],
          fields: [{ title: 'priority', value: '1' }],
          send_resolved: true,
        },
      ],
    };

    expect(formValuesToCloudReceiver(formValues, defaults)).toEqual(expected);
  });
});

describe('grafanaReceiverToFormValues', () => {
  const { googlechat, slack, sns } = grafanaAlertNotifiers;

  it('should convert fields from settings and secureFields', () => {
    const slackReceiver: GrafanaManagedContactPoint = {
      name: 'slack-receiver',
      grafana_managed_receiver_configs: [
        {
          type: slack.type,
          settings: {
            recipient: '#alerting-ops',
          },
          secureFields: {
            token: true,
          },
        },
      ],
    };

    const [formValues, _] = grafanaReceiverToFormValues(slackReceiver, grafanaAlertNotifiersMock);
    expect(formValues.items[0].type).toBe(slack.type);
    expect(formValues.items[0].settings.recipient).toBe('#alerting-ops');
    expect(formValues.items[0].secureFields.token).toBe(true);
    expect(formValues.items[0].secureSettings).toEqual({});
  });

  it('should convert nested settings and secureFields', () => {
    const snsReceiver: GrafanaManagedContactPoint = {
      name: 'sns-receiver',
      grafana_managed_receiver_configs: [
        {
          type: sns.type,
          settings: {
            api_url: 'https://sns.example.com/',
            phone_number: '+1234567890',
            sigv4: { region: 'us-east-1' },
          },
          secureFields: {
            'sigv4.access_key': true,
            'sigv4.secret_key': true,
          },
        },
      ],
    };

    const [formValues, _] = grafanaReceiverToFormValues(snsReceiver, grafanaAlertNotifiersMock);

    expect(formValues.items[0].settings.api_url).toBe('https://sns.example.com/');
    expect(formValues.items[0].settings.phone_number).toBe('+1234567890');
    expect(formValues.items[0].settings.sigv4.region).toBe('us-east-1');
    expect(formValues.items[0].secureFields['sigv4.access_key']).toBe(true);
    expect(formValues.items[0].secureFields['sigv4.secret_key']).toBe(true);
  });

  // Some receivers have migrated options that are now marked as secure but were standard fields in the past
  // We need to handle the case where the field is still present in settings but marked as secure
  it('should convert fields from settings to secureSettings for migrated options', () => {
    const googleChatReceiver: GrafanaManagedContactPoint = {
      name: 'googlechat-receiver',
      grafana_managed_receiver_configs: [
        {
          type: googlechat.type,
          settings: {
            url: 'https://googlechat.example.com/',
          },
        },
      ],
    };

    const [formValues, _] = grafanaReceiverToFormValues(googleChatReceiver, grafanaAlertNotifiersMock);
    expect(formValues.items[0].secureSettings.url).toBe('https://googlechat.example.com/');
    expect(formValues.items[0].settings.url).toBeUndefined();
  });
});

describe('convertJsonToJiraField', () => {
  it('should convert nested objects to JSON strings ', () => {
    const input = {
      fields: {
        key1: { nestedKey1: 'nestedValue1' },
        key2: { nestedKey2: 'nestedValue2' },
      },
    };
    const expectedOutput = {
      fields: {
        key1: '{"nestedKey1":"nestedValue1"}',
        key2: '{"nestedKey2":"nestedValue2"}',
      },
    };
    const result = convertJsonToJiraField(input);
    expect(result).toEqual(expectedOutput);
  });

  it('should leave non-object values unchanged ', () => {
    const input = {
      fields: {
        key1: 'value1',
        key2: 123,
        key3: true,
      },
    };
    const result = convertJsonToJiraField(input);
    expect(result).toEqual(input);
  });

  it('should handle fields object with mixed types', () => {
    const input = {
      fields: {
        key1: 'value1',
        key2: { nestedKey2: 'nestedValue2' },
        key3: 123,
        key4: true,
      },
    };
    const expectedOutput = {
      fields: {
        key1: 'value1',
        key2: '{"nestedKey2":"nestedValue2"}',
        key3: 123,
        key4: true,
      },
    };
    const result = convertJsonToJiraField(input);
    expect(result).toEqual(expectedOutput);
  });
});

describe('convertJiraFieldToJson', () => {
  it('should convert stringified objects to nested objects ', () => {
    const input = {
      fields: {
        key1: '{"nestedKey1":{"a":2,"c":[1,2,3 ]}}',
        key2: '{"nestedKey2":"nestedValue2"}',
      },
    };
    const expectedOutput = {
      fields: {
        key1: { nestedKey1: { a: 2, c: [1, 2, 3] } },
        key2: { nestedKey2: 'nestedValue2' },
      },
    };
    const result = convertJiraFieldToJson(input);
    expect(result).toEqual(expectedOutput);
  });

  it('should leave non-stringified values unchanged ', () => {
    const input = {
      fields: {
        key1: 'value1',
        key2: 123,
        key3: true,
      },
    };
    const result = convertJiraFieldToJson(input);
    expect(result).toEqual(input);
  });

  it('should handle fields object with mixed types ', () => {
    const input = {
      fields: {
        key1: 'value1',
        key2: '{"nestedKey2":"nestedValue2"}',
        key3: 123,
        key4: true,
      },
    };
    const expectedOutput = {
      fields: {
        key1: 'value1',
        key2: { nestedKey2: 'nestedValue2' },
        key3: 123,
        key4: true,
      },
    };
    const result = convertJiraFieldToJson(input);
    expect(result).toEqual(expectedOutput);
  });
});
