import React, { useEffect, useRef, useState } from 'react';
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
import { FormHandles } from '@unform/core';
import { Form } from '@unform/web';
import 'jest-styled-components';
import { Observable } from 'rxjs';
import '@testing-library/jest-dom';
import { SelectAutocomplete } from './index';

const fetchGroupMock = (): Observable<any> =>
   new Observable((observable) => {
      setTimeout(() => {
         observable.next([
            { disabled: true, name: 'Bairro', title: true },
            { name: 'Mogi das cruzes', value: { id: 'Mogi das cruzes' } },
            { name: 'Suzano', value: { id: 'Suzano' } },
            { disabled: true, name: 'Cidade', title: true },
            { name: 'São Paulo', value: { id: 'São Paulo' } },
            { name: 'Suzano', value: { id: 'Suzano' } },
            { name: 'Guarulhos', value: { id: 'Guarulhos' } },
         ]);
         observable.complete();
         observable.unsubscribe();
      }, 10);
   });

describe('SelectAutocomplete', () => {
   it('snapshot - open/close', () => {
      function Component(): any {
         return (
            <Form onSubmit={() => {}}>
               <SelectAutocomplete fetch={fetchGroupMock} name="autocomplete" getValue={(v) => v} />
            </Form>
         );
      }

      const { container } = render(<Component />);

      expect(container.firstChild).toMatchSnapshot();

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      expect(container.firstChild).toMatchSnapshot();
   });

   it('snapshot - with title', () => {
      function Component(): any {
         return (
            <Form onSubmit={() => {}}>
               <SelectAutocomplete
                  fetch={fetchGroupMock}
                  name="autocomplete"
                  title="Select AutoComplete"
                  getValue={(v) => v}
               />
            </Form>
         );
      }

      const { container } = render(<Component />);

      expect(container.firstChild).toMatchSnapshot();
   });

   it('snapshot - with error', () => {
      function Component(): any {
         return (
            <Form onSubmit={() => {}}>
               <SelectAutocomplete
                  fetch={fetchGroupMock}
                  title="Select AutoComplete"
                  getValue={(v) => v}
                  error="error"
               />
            </Form>
         );
      }

      const { container } = render(<Component />);

      expect(container.firstChild).toMatchSnapshot();
   });

   it('unform - check text in label initial value with options', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: 'Levi' }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: 'Eren' },
                     { name: 'Levi', value: 'Levi' },
                     { name: 'Rainer', value: 'Rainer' },
                     { name: 'Falco', value: 'Falco' },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Levi');
   });

   it('unform - check text in label initial value with options object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: { id: 'Levi' } }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: { id: 'Eren' } },
                     { name: 'Levi', value: { id: 'Levi' } },
                     { name: 'Rainer', value: { id: 'Rainer' } },
                     { name: 'Falco', value: { id: 'Falco' } },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Levi');
   });

   it('unform - check text in label initial value with async fetch object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: { id: 'Mogi das cruzes' } }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  initialFetch={[{ name: 'Mogi das cruzes', value: { id: 'Mogi das cruzes' } }]}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Mogi das cruzes');
   });

   it('unform - check text in label on select with options', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: 'Eren' },
                     { name: 'Levi', value: 'Levi' },
                     { name: 'Rainer', value: 'Rainer' },
                     { name: 'Falco', value: 'Falco' },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('text-selected-label'));

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Levi');
   });

   it('unform - check text in label on select with options object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: { id: 'Eren' } },
                     { name: 'Levi', value: { id: 'Levi' } },
                     { name: 'Rainer', value: { id: 'Rainer' } },
                     { name: 'Falco', value: { id: 'Falco' } },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Levi');
   });

   it('unform - check text in label on select with async fetch object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });

      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Mogi das cruzes');
   });

   it('unform - onSubmit with options', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: 'Eren' },
                     { name: 'Levi', value: 'Levi' },
                     { name: 'Rainer', value: 'Rainer' },
                     { name: 'Falco', value: 'Falco' },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: 'Levi',
      });
   });

   it('unform - onSubmit with options object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: { id: 'Eren' } },
                     { name: 'Levi', value: { id: 'Levi' } },
                     { name: 'Rainer', value: { id: 'Rainer' } },
                     { name: 'Falco', value: { id: 'Falco' } },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: { id: 'Levi' },
      });
   });

   it('unform - onSubmit with async fetch object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: { id: 'Mogi das cruzes' },
      });
   });

   it('unform - onSubmit with initial options', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: 'Levi' }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: 'Eren' },
                     { name: 'Levi', value: 'Levi' },
                     { name: 'Rainer', value: 'Rainer' },
                     { name: 'Falco', value: 'Falco' },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: 'Levi',
      });
   });

   it('unform - onSubmit with initial options object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: { id: 'Levi' } }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: { id: 'Eren' } },
                     { name: 'Levi', value: { id: 'Levi' } },
                     { name: 'Rainer', value: { id: 'Rainer' } },
                     { name: 'Falco', value: { id: 'Falco' } },
                  ]}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: { id: 'Levi' },
      });
   });

   it('unform - onSubmit with initial async fetch object', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: { id: 'Mogi das cruzes' } }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  initialFetch={[{ name: 'Mogi das cruzes', value: { id: 'Mogi das cruzes' } }]}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: { id: 'Mogi das cruzes' },
      });
   });

   it('unform - clear form and check label', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         const formRef = useRef<FormHandles>();

         return (
            <Form
               ref={formRef}
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: { id: 'Mogi das cruzes' } }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  initialFetch={[{ name: 'Mogi das cruzes', value: { id: 'Mogi das cruzes' } }]}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="button" data-testid="reset" onClick={() => formRef.current.reset()} />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      // Deve mostrar string de label
      fireEvent.input(screen.getByTestId('select-autocomplete--input'), { target: { value: 'E' } });
      fireEvent.focus(screen.getByTestId('select-autocomplete--input'));

      // Click first option
      await waitFor(() => !!screen.getByTestId('select-autocomplete-list--row-1'));
      fireEvent.click(screen.getByTestId('select-autocomplete-list--row-1'));

      fireEvent.click(screen.getByTestId('reset'));
      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: null,
      });

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('');
   });

   it('unform - set async data form and check label', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         const formRef = useRef<FormHandles>();

         const [initialOptions, setInitialOptions] = useState([{ name: 'Suzano', value: { id: 'Suzano' } }]);

         useEffect(() => {
            setInitialOptions([{ name: 'Mogi das cruzes', value: { id: 'Mogi das cruzes' } }]);
            formRef.current.setFieldValue('autocomplete', { id: 'Mogi das cruzes' });
         }, []);

         return (
            <Form
               ref={formRef}
               onSubmit={(data) => {
                  onSubmit(data);
               }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  fetch={fetchGroupMock}
                  initialFetch={initialOptions}
                  renderInput={(item: any) => (
                     <div>
                        {item.title && <b>{item.name}</b>}
                        {!item.title && <>{item.name}</>}
                     </div>
                  )}
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      fireEvent.click(screen.getByTestId('submit'));

      expect(onSubmit).toHaveBeenCalledWith({
         autocomplete: { id: 'Mogi das cruzes' },
      });

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('Mogi das cruzes');
   });

   it('unform - isClearable', async () => {
      const onSubmit = jest.fn();

      function Component(): any {
         return (
            <Form
               onSubmit={(data) => {
                  onSubmit(data);
               }}
               initialData={{ autocomplete: 'Levi' }}
            >
               <SelectAutocomplete
                  name="autocomplete"
                  options={[
                     { name: 'Eren', value: 'Eren' },
                     { name: 'Levi', value: 'Levi' },
                     { name: 'Rainer', value: 'Rainer' },
                     { name: 'Falco', value: 'Falco' },
                  ]}
                  isClearable
               />
               <button type="submit" data-testid="submit" />
            </Form>
         );
      }

      render(<Component />);

      fireEvent.click(screen.getByTestId('select-autocomplete--clear'));

      fireEvent.click(screen.getByTestId('submit'));

      expect(screen.getByTestId('text-selected-label').innerHTML).toEqual('');
   });
});
