import {browserHistory} from 'react-router';

import {enforceActOnUseLegacyStoreHook, mountWithTheme} from 'sentry-test/enzyme';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {act} from 'sentry-test/reactTestingLibrary';

import * as pageFilters from 'sentry/actionCreators/pageFilters';
import OrganizationStore from 'sentry/stores/organizationStore';
import ProjectsStore from 'sentry/stores/projectsStore';
import TeamStore from 'sentry/stores/teamStore';
import {MEPSettingProvider} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
import {OrganizationContext} from 'sentry/views/organizationContext';
import PerformanceContent from 'sentry/views/performance/content';
import {DEFAULT_MAX_DURATION} from 'sentry/views/performance/trends/utils';
import {RouteContext} from 'sentry/views/routeContext';

const FEATURES = ['performance-view'];

function WrappedComponent({organization, isMEPEnabled = false, router}) {
  return (
    <RouteContext.Provider
      value={{
        location,
        params: {},
        router,
        routes: [],
      }}
    >
      <OrganizationContext.Provider value={organization}>
        <MEPSettingProvider _isMEPEnabled={isMEPEnabled}>
          <PerformanceContent organization={organization} location={router.location} />
        </MEPSettingProvider>
      </OrganizationContext.Provider>
    </RouteContext.Provider>
  );
}

function initializeData(projects, query, features = FEATURES) {
  const organization = TestStubs.Organization({
    features,
    projects,
  });
  const initialData = initializeOrg({
    organization,
    router: {
      location: {
        pathname: '/test',
        query: query || {},
      },
    },
  });
  act(() => void OrganizationStore.onUpdate(initialData.organization, {replace: true}));
  act(() => ProjectsStore.loadInitialData(initialData.organization.projects));
  return initialData;
}

function initializeTrendsData(query, addDefaultQuery = true) {
  const projects = [
    TestStubs.Project({id: '1', firstTransactionEvent: false}),
    TestStubs.Project({id: '2', firstTransactionEvent: true}),
  ];
  const organization = TestStubs.Organization({
    FEATURES,
    projects,
  });

  const otherTrendsQuery = addDefaultQuery
    ? {
        query: `tpm():>0.01 transaction.duration:>0 transaction.duration:<${DEFAULT_MAX_DURATION}`,
      }
    : {};

  const initialData = initializeOrg({
    organization,
    router: {
      location: {
        pathname: '/test',
        query: {
          ...otherTrendsQuery,
          ...query,
        },
      },
    },
  });
  act(() => ProjectsStore.loadInitialData(initialData.organization.projects));
  return initialData;
}

describe('Performance > Content', function () {
  enforceActOnUseLegacyStoreHook();

  beforeEach(function () {
    act(() => void TeamStore.loadInitialData([], false, null));
    browserHistory.push = jest.fn();
    jest.spyOn(pageFilters, 'updateDateTime');

    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/projects/',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/tags/',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-stats/',
      body: {data: [[123, []]]},
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-histogram/',
      body: {'transaction.duration': [{bin: 0, count: 1000}]},
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/users/',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/recent-searches/',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/recent-searches/',
      method: 'POST',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/sdk-updates/',
      body: [],
    });
    MockApiClient.addMockResponse({
      url: '/prompts-activity/',
      body: {},
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/eventsv2/',
      body: {
        meta: {
          user: 'string',
          transaction: 'string',
          'project.id': 'integer',
          tpm: 'number',
          p50: 'number',
          p95: 'number',
          failure_rate: 'number',
          apdex_300: 'number',
          count_unique_user: 'number',
          count_miserable_user_300: 'number',
          user_misery_300: 'number',
        },
        data: [
          {
            transaction: '/apple/cart',
            'project.id': 1,
            user: 'uhoh@example.com',
            tpm: 30,
            p50: 100,
            p95: 500,
            failure_rate: 0.1,
            apdex_300: 0.6,
            count_unique_user: 1000,
            count_miserable_user_300: 122,
            user_misery_300: 0.114,
          },
        ],
      },
      match: [
        (_, options) => {
          if (!options.hasOwnProperty('query')) {
            return false;
          }
          if (!options.query.hasOwnProperty('field')) {
            return false;
          }
          return !options.query.field.includes('team_key_transaction');
        },
      ],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/eventsv2/',
      body: {
        meta: {
          user: 'string',
          transaction: 'string',
          'project.id': 'integer',
          tpm: 'number',
          p50: 'number',
          p95: 'number',
          failure_rate: 'number',
          apdex_300: 'number',
          count_unique_user: 'number',
          count_miserable_user_300: 'number',
          user_misery_300: 'number',
        },
        data: [
          {
            team_key_transaction: 1,
            transaction: '/apple/cart',
            'project.id': 1,
            user: 'uhoh@example.com',
            tpm: 30,
            p50: 100,
            p95: 500,
            failure_rate: 0.1,
            apdex_300: 0.6,
            count_unique_user: 1000,
            count_miserable_user_300: 122,
            user_misery_300: 0.114,
          },
          {
            team_key_transaction: 0,
            transaction: '/apple/checkout',
            'project.id': 1,
            user: 'uhoh@example.com',
            tpm: 30,
            p50: 100,
            p95: 500,
            failure_rate: 0.1,
            apdex_300: 0.6,
            count_unique_user: 1000,
            count_miserable_user_300: 122,
            user_misery_300: 0.114,
          },
        ],
      },
      match: [
        (_, options) => {
          if (!options.hasOwnProperty('query')) {
            return false;
          }
          if (!options.query.hasOwnProperty('field')) {
            return false;
          }
          return options.query.field.includes('team_key_transaction');
        },
      ],
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-meta/',
      body: {
        count: 2,
      },
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-trends/',
      body: {
        stats: {},
        events: {meta: {}, data: []},
      },
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-trends-stats/',
      body: {
        stats: {},
        events: {meta: {}, data: []},
      },
    });
    MockApiClient.addMockResponse({
      url: '/organizations/org-slug/events-vitals/',
      body: {
        'measurements.lcp': {
          poor: 1,
          meh: 2,
          good: 3,
          total: 6,
          p75: 4500,
        },
      },
    });
    MockApiClient.addMockResponse({
      method: 'GET',
      url: `/organizations/org-slug/key-transactions-list/`,
      body: [],
    });
  });

  afterEach(function () {
    MockApiClient.clearMockResponses();
    act(() => ProjectsStore.reset());
    pageFilters.updateDateTime.mockRestore();
  });

  it('renders basic UI elements', async function () {
    const projects = [TestStubs.Project({firstTransactionEvent: true})];
    const data = initializeData(projects, {});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    // performance landing container
    expect(wrapper.find('div[data-test-id="performance-landing-v3"]').exists()).toBe(
      true
    );

    // No onboarding should show.
    expect(wrapper.find('Onboarding')).toHaveLength(0);

    // Table should render.
    expect(wrapper.find('Table')).toHaveLength(1);

    wrapper.unmount();
  });

  it('renders onboarding state when the selected project has no events', async function () {
    const projects = [
      TestStubs.Project({id: 1, firstTransactionEvent: false}),
      TestStubs.Project({id: 2, firstTransactionEvent: true}),
    ];
    const data = initializeData(projects, {project: [1]});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    // onboarding should show.
    expect(wrapper.find('Onboarding')).toHaveLength(1);

    // Table should not show.
    expect(wrapper.find('Table')).toHaveLength(0);
    wrapper.unmount();
  });

  it('does not render onboarding for "my projects"', async function () {
    const projects = [
      TestStubs.Project({id: '1', firstTransactionEvent: false}),
      TestStubs.Project({id: '2', firstTransactionEvent: true}),
    ];
    const data = initializeData(projects, {project: ['-1']});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    expect(wrapper.find('Onboarding')).toHaveLength(0);
    wrapper.unmount();
  });

  it('forwards conditions to transaction summary', async function () {
    const projects = [TestStubs.Project({id: '1', firstTransactionEvent: true})];
    const data = initializeData(projects, {project: ['1'], query: 'sentry:yes'});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    const link = wrapper.find('[data-test-id="grid-editable"] GridBody Link').at(0);
    link.simulate('click', {button: 0});

    expect(data.router.push).toHaveBeenCalledWith(
      expect.objectContaining({
        query: expect.objectContaining({
          transaction: '/apple/cart',
          query: 'sentry:yes',
        }),
      })
    );
    wrapper.unmount();
  });

  it('Default period for trends does not call updateDateTime', async function () {
    const data = initializeTrendsData({query: 'tag:value'}, false);
    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    expect(pageFilters.updateDateTime).toHaveBeenCalledTimes(0);
    wrapper.unmount();
  });

  it('Navigating to trends does not modify statsPeriod when already set', async function () {
    const data = initializeTrendsData({
      query: `tpm():>0.005 transaction.duration:>10 transaction.duration:<${DEFAULT_MAX_DURATION} api`,
      statsPeriod: '24h',
    });

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    const trendsLink = wrapper.find('[data-test-id="landing-header-trends"]').at(0);
    trendsLink.simulate('click');

    expect(pageFilters.updateDateTime).toHaveBeenCalledTimes(0);

    expect(browserHistory.push).toHaveBeenCalledWith(
      expect.objectContaining({
        pathname: '/organizations/org-slug/performance/trends/',
        query: {
          query: `tpm():>0.005 transaction.duration:>10 transaction.duration:<${DEFAULT_MAX_DURATION}`,
          statsPeriod: '24h',
        },
      })
    );
    wrapper.unmount();
  });

  it('Default page (transactions) without trends feature will not update filters if none are set', async function () {
    const projects = [
      TestStubs.Project({id: 1, firstTransactionEvent: false}),
      TestStubs.Project({id: 2, firstTransactionEvent: true}),
    ];
    const data = initializeData(projects, {view: undefined});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    expect(browserHistory.push).toHaveBeenCalledTimes(0);
    wrapper.unmount();
  });

  it('Default page (transactions) with trends feature will not update filters if none are set', async function () {
    const data = initializeTrendsData({view: undefined}, false);

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    expect(browserHistory.push).toHaveBeenCalledTimes(0);
    wrapper.unmount();
  });

  it('Tags are replaced with trends default query if navigating to trends', async function () {
    const data = initializeTrendsData({query: 'device.family:Mac'}, false);

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    const trendsLink = wrapper.find('[data-test-id="landing-header-trends"]').at(0);
    trendsLink.simulate('click');

    expect(browserHistory.push).toHaveBeenCalledWith(
      expect.objectContaining({
        pathname: '/organizations/org-slug/performance/trends/',
        query: {
          query: `tpm():>0.01 transaction.duration:>0 transaction.duration:<${DEFAULT_MAX_DURATION}`,
        },
      })
    );
    wrapper.unmount();
  });

  it('Display Create Sample Transaction Button', async function () {
    const projects = [
      TestStubs.Project({id: 1, firstTransactionEvent: false}),
      TestStubs.Project({id: 2, firstTransactionEvent: false}),
    ];
    const data = initializeData(projects, {view: undefined});

    const wrapper = mountWithTheme(
      <WrappedComponent organization={data.organization} router={data.router} />,
      data.routerContext
    );

    await act(async () => {
      await tick();
      wrapper.update();
    });

    expect(
      wrapper.find('Button[data-test-id="create-sample-transaction-btn"]').exists()
    ).toBe(true);
    wrapper.unmount();
  });
});
