import {ChromeTraceProfile} from 'sentry/utils/profiling/profile/chromeTraceProfile';
import {EventedProfile} from 'sentry/utils/profiling/profile/eventedProfile';
import {
  importDroppedProfile,
  importProfile,
} from 'sentry/utils/profiling/profile/importProfile';
import {JSSelfProfile} from 'sentry/utils/profiling/profile/jsSelfProfile';
import {SampledProfile} from 'sentry/utils/profiling/profile/sampledProfile';

import {SentrySampledProfile} from './sentrySampledProfile';
import {makeSentrySampledProfile} from './sentrySampledProfile.specutil';

describe('importProfile', () => {
  it('imports evented profile', () => {
    const eventedProfile: Profiling.EventedProfile = {
      name: 'profile',
      startValue: 0,
      endValue: 1000,
      threadID: 0,
      unit: 'milliseconds',
      type: 'evented',
      events: [],
    };

    const imported = importProfile(
      {
        activeProfileIndex: 0,
        profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
        profiles: [eventedProfile],
        projectID: 1,
        shared: {
          frames: [],
        },
        metadata: {} as Profiling.Schema['metadata'],
      },
      ''
    );

    expect(imported.profiles[0]).toBeInstanceOf(EventedProfile);
  });
  it('imports sampled profile', () => {
    const sampledProfile: Profiling.SampledProfile = {
      name: 'profile',
      startValue: 0,
      endValue: 1000,
      threadID: 0,
      unit: 'milliseconds',
      type: 'sampled',
      weights: [],
      samples: [],
    };

    const imported = importProfile(
      {
        activeProfileIndex: 0,
        profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
        profiles: [sampledProfile],
        projectID: 1,
        shared: {
          frames: [],
        },
        metadata: {} as Profiling.Schema['metadata'],
      },
      ''
    );

    expect(imported.profiles[0]).toBeInstanceOf(SampledProfile);
  });

  it('imports typescript profile', () => {
    const typescriptProfile: ChromeTrace.ArrayFormat = [
      {
        ph: 'B',
        ts: 1000,
        cat: 'program',
        pid: 0,
        tid: 0,
        name: 'createProgram',
        args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
      },
      {
        ph: 'E',
        ts: 2000,
        cat: 'program',
        pid: 0,
        tid: 0,
        name: 'createProgram',
        args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
      },
    ];

    const imported = importProfile(typescriptProfile, '');
    expect(imported.profiles[0]).toBeInstanceOf(ChromeTraceProfile);
  });
  it('imports a nodejs profile', () => {
    const nodeProfile: Profiling.NodeProfile = {
      name: 'profile',
      startValue: 0,
      endValue: 1000,
      threadID: 0,
      unit: 'milliseconds',
      type: 'sampled',
      weights: [],
      samples: [],
      frames: [],
    };

    const imported = importProfile([nodeProfile, {}], '');
    expect(imported.profiles[0]).toBeInstanceOf(SampledProfile);
  });
  it('imports JS self profile from schema', () => {
    const jsSelfProfile: JSSelfProfiling.Trace = {
      resources: ['app.js', 'vendor.js'],
      frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
      samples: [
        {
          timestamp: 0,
        },
        {
          timestamp: 1000,
          stackId: 0,
        },
      ],
      stacks: [
        {
          frameId: 0,
        },
      ],
    };

    const imported = importProfile(
      {
        activeProfileIndex: 0,
        profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
        profiles: [jsSelfProfile],
        projectID: 1,
        metadata: {} as Profiling.Schema['metadata'],
        shared: {
          frames: [],
        },
      },
      ''
    );

    expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  });

  it('imports JS self profile from raw Profiling output', () => {
    const jsSelfProfile: JSSelfProfiling.Trace = {
      resources: ['app.js', 'vendor.js'],
      frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
      samples: [
        {
          timestamp: 0,
        },
        {
          timestamp: 1000,
          stackId: 0,
        },
      ],
      stacks: [
        {
          frameId: 0,
        },
      ],
    };

    const imported = importProfile(jsSelfProfile, 'profile');

    expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  });

  it('imports sentry sampled profile', () => {
    const sentrySampledProfile = makeSentrySampledProfile();

    const imported = importProfile(sentrySampledProfile, 'profile');

    expect(imported.profiles[0]).toBeInstanceOf(SentrySampledProfile);
  });

  it('throws on unrecognized profile type', () => {
    expect(() =>
      importProfile(
        // @ts-ignore
        {name: 'profile', activeProfileIndex: 0, profiles: [{type: 'unrecognized'}]},
        ''
      )
    ).toThrow();
  });
});

describe('importDroppedProfile', () => {
  beforeEach(() => {
    jest.restoreAllMocks();
  });
  it('throws if file has no string contents', async () => {
    // @ts-ignore we are just setting null on the file, we are not actually reading it because our event is mocked
    const file = new File([null], 'test.tsx');

    const reader = new FileReader();

    jest.spyOn(window, 'FileReader').mockImplementation(() => reader);
    jest.spyOn(reader, 'readAsText').mockImplementation(() => {
      const loadEvent = new CustomEvent('load', {
        detail: {target: {result: null}},
      });

      reader.dispatchEvent(loadEvent);
    });

    await expect(importDroppedProfile(file)).rejects.toEqual(
      'Failed to read string contents of input file'
    );
  });

  it('throws if FileReader errors', async () => {
    const file = new File(['{json: true}'], 'test.tsx');

    const reader = new FileReader();

    jest.spyOn(window, 'FileReader').mockImplementation(() => reader);
    jest.spyOn(reader, 'readAsText').mockImplementation(() => {
      const loadEvent = new CustomEvent('error', {
        detail: {target: {result: null}},
      });

      reader.dispatchEvent(loadEvent);
    });

    await expect(importDroppedProfile(file)).rejects.toEqual(
      'Failed to read string contents of input file'
    );
  });

  it('throws if contents are not valid JSON', async () => {
    const file = new File(['{"json": true'], 'test.tsx');
    await expect(importDroppedProfile(file)).rejects.toBeInstanceOf(Error);
  });

  it('imports dropped schema file', async () => {
    const schema: Profiling.Schema = {
      activeProfileIndex: 0,
      profileID: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
      profiles: [
        {
          name: 'profile',
          startValue: 0,
          endValue: 1000,
          threadID: 0,
          unit: 'milliseconds',
          type: 'sampled',
          weights: [],
          samples: [],
        },
      ],
      projectID: 1,
      shared: {
        frames: [],
      },
      metadata: {} as Profiling.Schema['metadata'],
    };
    const file = new File([JSON.stringify(schema)], 'test.tsx');
    const imported = await importDroppedProfile(file);

    expect(imported.profiles[0]).toBeInstanceOf(SampledProfile);
  });

  it('imports dropped typescript profile', async () => {
    const typescriptProfile: ChromeTrace.ArrayFormat = [
      {
        ph: 'B',
        ts: 1000,
        cat: 'program',
        pid: 0,
        tid: 0,
        name: 'createProgram',
        args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
      },
      {
        ph: 'E',
        ts: 2000,
        cat: 'program',
        pid: 0,
        tid: 0,
        name: 'createProgram',
        args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
      },
    ];

    const file = new File([JSON.stringify(typescriptProfile)], 'test.tsx');
    const imported = await importDroppedProfile(file);

    expect(imported.profiles[0]).toBeInstanceOf(ChromeTraceProfile);
  });

  it('imports dropped JS self profile', async () => {
    const jsSelfProfile: JSSelfProfiling.Trace = {
      resources: ['app.js', 'vendor.js'],
      frames: [{name: 'ReactDOM.render', line: 1, column: 1, resourceId: 0}],
      samples: [
        {
          timestamp: 0,
        },
        {
          timestamp: 1000,
          stackId: 0,
        },
      ],
      stacks: [
        {
          frameId: 0,
        },
      ],
    };

    const file = new File([JSON.stringify(jsSelfProfile)], 'test.tsx');
    const imported = await importDroppedProfile(file);

    expect(imported.profiles[0]).toBeInstanceOf(JSSelfProfile);
  });
});
