import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';

import ExternalIssueForm from 'sentry/components/group/externalIssueForm';

jest.mock('lodash/debounce', () => {
  const debounceMap = new Map();
  const mockDebounce =
    (fn, timeout) =>
    (...args) => {
      if (debounceMap.has(fn)) {
        clearTimeout(debounceMap.get(fn));
      }
      debounceMap.set(
        fn,
        setTimeout(() => {
          fn.apply(fn, args);
          debounceMap.delete(fn);
        }, timeout)
      );
    };
  return mockDebounce;
});

describe('ExternalIssueForm', () => {
  let group, integration, formConfig;
  const onChange = jest.fn();
  beforeEach(() => {
    MockApiClient.clearMockResponses();
    group = TestStubs.Group();
    integration = TestStubs.GitHubIntegration({externalIssues: []});
  });

  afterEach(() => {
    jest.useRealTimers();
    jest.clearAllMocks();
  });

  const renderComponent = async (action = 'Create') => {
    MockApiClient.addMockResponse({
      url: `/groups/${group.id}/integrations/${integration.id}/`,
      body: formConfig,
      match: [MockApiClient.matchQuery({action: 'create'})],
    });
    const wrapper = render(
      <ExternalIssueForm
        Body={p => p.children}
        Header={p => p.children}
        group={group}
        integration={integration}
        onChange={onChange}
      />
    );
    await userEvent.click(screen.getByText(action));
    return wrapper;
  };

  describe('create', () => {
    // TODO: expand tests
    beforeEach(() => {
      formConfig = {
        createIssueConfig: [],
      };
      MockApiClient.addMockResponse({
        url: `/groups/${group.id}/integrations/${integration.id}/`,
        body: formConfig,
      });
    });
    it('renders', async () => {
      const {container} = await renderComponent();
      expect(container).toSnapshot();
    });
  });
  describe('link', () => {
    let externalIssueField, getFormConfigRequest;
    beforeEach(() => {
      externalIssueField = {
        name: 'externalIssue',
        default: '',
        required: true,
        choices: [],
        url: '/search',
        label: 'Issue',
        type: 'select',
      };
      formConfig = {
        status: 'active',
        name: 'scefali',
        domainName: 'github.com/scefali',
        linkIssueConfig: [
          {
            url: '/search',
            required: true,
            name: 'repo',
            default: 'scefali/test',
            updatesForm: true,
            choices: [
              ['scefali/test', 'test'],
              ['scefali/ZeldaBazaar', 'ZeldaBazaar'],
            ],
            type: 'select',
            label: 'GitHub Repository',
          },
          externalIssueField,
          {
            help: "Leave blank if you don't want to add a comment to the GitHub issue.",
            default: 'Default Value',
            required: false,
            label: 'Comment',
            type: 'textarea',
            name: 'comment',
          },
        ],
        accountType: 'User',
        provider: {
          canAdd: true,
          aspects: {
            disable_dialog: {
              body: 'Before deleting this integration, you must uninstall this integration from GitHub. After uninstalling, your integration will be disabled at which point you can choose to delete this integration.',
              actionText: 'Visit GitHub',
            },
            removal_dialog: {
              body: 'Deleting this integration will delete all associated repositories and commit data. This action cannot be undone. Are you sure you want to delete your integration?',
              actionText: 'Delete',
            },
          },
          features: ['commits', 'issue-basic'],
          canDisable: true,
          key: 'github',
          name: 'GitHub',
        },
        id: '5',
      };
      getFormConfigRequest = MockApiClient.addMockResponse({
        url: `/groups/${group.id}/integrations/${integration.id}/`,
        body: formConfig,
        match: [MockApiClient.matchQuery({action: 'link'})],
      });
    });

    it('renders and loads options', async () => {
      const {container} = await renderComponent('Link');
      expect(getFormConfigRequest).toHaveBeenCalled();
      expect(container).toSnapshot();
    });

    describe('options loaded', () => {
      beforeEach(() => {
        const mockFetchPromise = () =>
          new Promise(resolve => {
            setTimeout(() => {
              resolve({
                json: () =>
                  Promise.resolve([
                    {
                      label: '#1337 ref(js): Convert Form to a FC',
                      value: 1337,
                    },
                    {
                      label: '#2345 perf: Make it faster',
                      value: 2345,
                    },
                  ]),
                ok: true,
              });
            }, 50);
          });

        window.fetch = jest.fn().mockImplementation(mockFetchPromise);

        MockApiClient.addMockResponse({
          url: `/groups/${group.id}/integrations/${integration.id}/?action=link`,
          body: formConfig,
        });
      });

      afterEach(() => {
        window.fetch.mockClear();
        delete window.fetch;
      });

      it('fast typing is debounced and uses trailing call when fetching data', async () => {
        await renderComponent('Link');
        jest.useFakeTimers();
        const textbox = screen.getByRole('textbox', {name: 'Issue'});
        await userEvent.click(textbox, {delay: null});
        await userEvent.type(textbox, 'faster', {delay: null});
        expect(window.fetch).toHaveBeenCalledTimes(0);
        jest.advanceTimersByTime(300);
        expect(window.fetch).toHaveBeenCalledTimes(1);
        expect(window.fetch).toHaveBeenCalledWith(
          '/search?field=externalIssue&query=faster&repo=scefali%2Ftest'
        );
        expect(await screen.findByText('#2345 perf: Make it faster')).toBeInTheDocument();
      });
    });
  });
});
