import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import {
  AddressConfirmOptions,
  MapboxAddressAutofill,
  MapboxHTMLEvent,
  Theme
} from '@mapbox/search-js-web';
import { AddressAutofillOptions, SearchSession } from '@mapbox/search-js-core';

import { AddressAutofill } from '../../src';
import { AddressAutofillRefType } from '../../src/components/AddressAutofill';

const OPTIONS: Partial<AddressAutofillOptions> = { language: 'de' };
const THEME: Theme = { variables: { fontFamily: 'Comic Sans MS ' } };
const CONFIRM_ON_BROWSER_AUTOFILL: AddressConfirmOptions = {
  minimap: true,
  sections: ['section-shipping']
};

const suggestFuncMock = jest.fn();
jest
  .spyOn(SearchSession.prototype, 'suggest')
  .mockImplementation(suggestFuncMock);

const unmockedFetch = global.fetch;

beforeAll(() => {
  /* eslint-disable-next-line */
  // @ts-ignore
  global.fetch = () => {
    Promise.resolve({
      json: () => Promise.resolve({ test: 'value' })
    });
  };
});

afterAll(() => {
  global.fetch = unmockedFetch;
});

describe('AddressAutofill', () => {
  it('renders', () => {
    const handleChange = jest.fn();
    const handleSuggest = jest.fn();
    const handleSuggestError = jest.fn();
    const handleRetrieve = jest.fn();
    const handleInterceptSearch = jest.fn();

    const ref = React.createRef<AddressAutofillRefType>();
    const { baseElement, rerender, unmount } = render(
      <form>
        <AddressAutofill
          ref={ref}
          accessToken="xyz"
          options={OPTIONS}
          theme={THEME}
          confirmOnBrowserAutofill={CONFIRM_ON_BROWSER_AUTOFILL}
          onChange={handleChange}
          onSuggest={handleSuggest}
          onSuggestError={handleSuggestError}
          onRetrieve={handleRetrieve}
          interceptSearch={handleInterceptSearch}
        >
          <input
            type="text"
            name="address"
            autoComplete="shipping street-address"
          />
        </AddressAutofill>
      </form>
    );

    const autofill = baseElement.querySelector<MapboxAddressAutofill>(
      'mapbox-address-autofill'
    );
    expect(autofill).toBeTruthy();

    expect(autofill.accessToken).toBe('xyz');
    expect(autofill.options).toEqual(OPTIONS);
    expect(autofill.theme).toEqual(THEME);
    expect(autofill.confirmOnBrowserAutofill).toEqual(
      CONFIRM_ON_BROWSER_AUTOFILL
    );
    expect(autofill.interceptSearch).toEqual(handleInterceptSearch);

    autofill.dispatchEvent(new MapboxHTMLEvent('input', 'foo'));
    expect(handleChange).toHaveBeenCalledTimes(1);

    // test interception that mutates search text
    handleInterceptSearch.mockImplementation((val) => val + ' main');
    fireEvent.input(autofill.input, { target: { value: '123' } });
    expect(handleInterceptSearch).toHaveBeenCalledTimes(1);
    expect(suggestFuncMock).toHaveBeenCalledWith('123 main', {
      language: 'de'
    });

    handleInterceptSearch.mockClear();
    suggestFuncMock.mockClear();

    // test interception that not sending the request returning `undefined` value
    handleInterceptSearch.mockImplementation(() => undefined);
    fireEvent.input(autofill.input, { target: { value: '123' } });
    expect(handleInterceptSearch).toHaveBeenCalledTimes(1);
    expect(suggestFuncMock).toHaveBeenCalledTimes(0);

    autofill.dispatchEvent(new MapboxHTMLEvent('suggest', { suggestions: [] }));
    expect(handleSuggest).toHaveBeenCalledTimes(1);

    autofill.dispatchEvent(
      new MapboxHTMLEvent('suggesterror', new Error('foo'))
    );
    expect(handleSuggestError).toHaveBeenCalledTimes(1);

    autofill.dispatchEvent(
      new MapboxHTMLEvent('retrieve', {
        suggestions: []
      })
    );
    expect(handleRetrieve).toHaveBeenCalledTimes(1);

    expect(ref.current).toBeTruthy();
    jest.spyOn(autofill, 'focus');
    ref.current.focus();
    expect(autofill.focus).toHaveBeenCalledTimes(1);

    rerender(
      <form>
        <AddressAutofill
          ref={ref}
          accessToken="xyz"
          options={OPTIONS}
          theme={THEME}
          confirmOnBrowserAutofill={CONFIRM_ON_BROWSER_AUTOFILL}
        >
          <input
            type="text"
            name="address"
            autoComplete="shipping street-address"
          />
        </AddressAutofill>
      </form>
    );

    const oldRef = ref.current;
    unmount();
    expect(oldRef.focus).toThrowError('AddressAutofill is not mounted');
  });
});
