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

const options = [
   { name: 'Eren', value: 'Eren' },
   { name: 'Levi', value: 'Levi' },
   { name: 'Rainer', value: 'Rainer' },
   { name: 'Falco', value: 'Falco' },
];

it('snapshot - default', () => {
   function Component(): any {
      return <ButtonsGroup options={options} />;
   }

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

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

it('snapshot - with title', () => {
   function Component(): any {
      return <ButtonsGroup title="ButtonGroup" options={options} />;
   }

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

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

it('snapshot - with error', () => {
   function Component(): any {
      return <ButtonsGroup title="ButtonGroup" options={options} error="Erro qualquer" />;
   }

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

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

it('snapshot - variant color', () => {
   function Component(): any {
      return <ButtonsGroup variant="secondary" options={options} />;
   }

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

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

it('snapshot - oneLine', () => {
   function Component(): any {
      return <ButtonsGroup oneLine options={options} />;
   }

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

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

it('snapshot - rounded', () => {
   function Component(): any {
      return <ButtonsGroup rounded options={options} />;
   }

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

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

it('snapshot - split', () => {
   function Component(): any {
      return <ButtonsGroup split options={options} />;
   }

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

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

it('snapshot - with initial value', () => {
   function Component(): any {
      const [value, setValue] = useState('Rainer');

      return (
         <>
            <ButtonsGroup value={value} onSelect={setValue} options={options} />
         </>
      );
   }

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

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

it('snapshot - with initial value multiSelection', () => {
   function Component(): any {
      const [value, setValue] = useState(['Rainer', 'Eren']);

      return (
         <>
            <ButtonsGroup value={value} onSelect={setValue} options={options} />
         </>
      );
   }

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

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

it('callback onSelect', async () => {
   const onChangeValue: any = jest.fn();

   function Component(): any {
      const [value, setValue] = useState();

      return (
         <ButtonsGroup
            options={options}
            title="Characters"
            value={value}
            onSelect={(v, v2) => {
               setValue(v);
               onChangeValue(v, v2);
            }}
         />
      );
   }

   render(<Component />);

   // Definir data
   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Rainer'));

   await waitFor(() => expect(onChangeValue).toHaveBeenCalled());
   expect(onChangeValue).toHaveBeenCalledWith('Levi', { name: 'Levi', value: 'Levi' });
   expect(onChangeValue).toHaveBeenCalledWith('Rainer', { name: 'Rainer', value: 'Rainer' });
});

it('check prop multiSelection', () => {
   const onChangeValue: any = jest.fn();

   function Component(): any {
      const [value, setValue] = useState();

      return (
         <ButtonsGroup
            options={options}
            value={value}
            onSelect={(v, v2) => {
               setValue(v);
               onChangeValue(v, v2);
            }}
            title="Characters"
            multiSelection
         />
      );
   }

   render(<Component />);

   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Falco'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Falco'));

   expect(onChangeValue).toHaveBeenCalled();
   expect(onChangeValue).toHaveBeenCalledWith(['Levi'], { name: 'Levi', value: 'Levi' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Falco'], { name: 'Falco', value: 'Falco' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Falco', 'Rainer'], { name: 'Rainer', value: 'Rainer' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Rainer'], { name: 'Falco', value: 'Falco' });
});

it('check prop allowEmpty', async () => {
   const onChangeValue: any = jest.fn();

   function Component(): any {
      const [value, setValue] = useState();

      return (
         <ButtonsGroup
            options={options}
            title="Characters"
            allowEmpty
            value={value}
            onSelect={(v) => {
               setValue(v);
               onChangeValue(v);
            }}
         />
      );
   }

   render(<Component />);

   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Rainer'));

   await waitFor(() => expect(onChangeValue).toHaveBeenCalled());
   expect(onChangeValue).toHaveBeenCalledWith(null);

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('check initial value', async () => {
   function Component(): any {
      const [value, setValue] = useState('Rainer');

      return <ButtonsGroup value={value} onSelect={setValue} options={options} />;
   }

   render(<Component />);

   await waitFor(() => screen.getByTestId('button-Eren'));

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('check initial value with multiSelection', () => {
   function Component(): any {
      const [value, setValue] = useState(['Rainer', 'Eren']);

      return <ButtonsGroup value={value} onSelect={setValue} options={options} multiSelection />;
   }

   render(<Component />);

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('unform - snapshot - default', () => {
   function Component(): any {
      return (
         <Form onSubmit={() => {}}>
            <ButtonsGroup name="button" options={options} />
         </Form>
      );
   }

   const { container } = render(<Component />);
   expect(container.firstChild).toMatchSnapshot();
});

it('unform - snapshot - with title', () => {
   function Component(): any {
      return (
         <Form onSubmit={() => {}}>
            <ButtonsGroup name="button" title="ButtonGroup" options={options} />
         </Form>
      );
   }

   const { container } = render(<Component />);
   expect(container.firstChild).toMatchSnapshot();
});

it('unform - snapshot - with error', () => {
   function Component(): any {
      const formRef = useRef(null);

      useEffect(() => {
         formRef.current.setFieldError('default', 'Field Invalid');
      }, []);

      return (
         <Form onSubmit={() => {}} ref={formRef}>
            <ButtonsGroup name="default" options={options} />
         </Form>
      );
   }

   const { container } = render(<Component />);
   expect(container.firstChild).toMatchSnapshot();
});

it('unform - snapshot - with error', () => {
   function Component(): any {
      const formRef = useRef(null);

      useEffect(() => {
         formRef.current.setFieldError('default', 'Field Invalid');
      }, []);

      return (
         <Form onSubmit={() => {}} ref={formRef}>
            <ButtonsGroup name="default" options={options} />
         </Form>
      );
   }

   const { container } = render(<Component />);
   expect(container.firstChild).toMatchSnapshot();
});

it('unform - check initial value', () => {
   function Component(): any {
      return (
         <>
            <Form onSubmit={() => {}} initialData={{ buttons: 'Rainer' }}>
               <ButtonsGroup name="buttons" options={options} />
            </Form>
         </>
      );
   }

   render(<Component />);

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('unform - check initial value with multiSelection', () => {
   function Component(): any {
      return (
         <>
            <Form onSubmit={() => {}} initialData={{ buttons: ['Rainer', 'Eren'] }}>
               <ButtonsGroup name="buttons" options={options} multiSelection />
            </Form>
         </>
      );
   }

   render(<Component />);

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'true');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('unform - clear form', () => {
   function Component(): any {
      const formRef = useRef<FormHandles>();

      function clearForm(): void {
         formRef.current.reset();
      }

      return (
         <>
            <Form onSubmit={() => {}} initialData={{ buttons: 'Rainer' }} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} />
               <button data-testid="clear" title="clear" onClick={clearForm} />
            </Form>
         </>
      );
   }

   render(<Component />);

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

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('unform - set data', () => {
   function Component(): any {
      const formRef = useRef<FormHandles>();

      function clearForm(): void {
         formRef.current.setFieldValue('buttons', 'Falco');
      }

      return (
         <>
            <Form onSubmit={() => {}} initialData={{ buttons: 'Rainer' }} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} />
               <button data-testid="clear" title="clear" onClick={clearForm} />
            </Form>
         </>
      );
   }

   render(<Component />);

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

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'true');
});

it('unform - callback onSelect', async () => {
   const onChangeValue = jest.fn();

   function Component(): any {
      return (
         <>
            <Form onSubmit={() => {}}>
               <ButtonsGroup
                  name="buttons"
                  options={options}
                  onSelect={(v, v2) => {
                     onChangeValue(v, v2);
                  }}
               />
            </Form>
         </>
      );
   }

   render(<Component />);

   // Definir data
   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Rainer'));

   await waitFor(() => expect(onChangeValue).toHaveBeenCalled());
   expect(onChangeValue).toHaveBeenCalledWith('Levi', { name: 'Levi', value: 'Levi' });
   expect(onChangeValue).toHaveBeenCalledWith('Rainer', { name: 'Rainer', value: 'Rainer' });
});

it('unform - check prop multiSelection', () => {
   const onChangeValue = jest.fn();

   function Component(): any {
      return (
         <Form onSubmit={() => {}}>
            <ButtonsGroup
               name="button"
               options={options}
               onSelect={(v, v2) => {
                  onChangeValue(v, v2);
               }}
               title="Characters"
               multiSelection
            />
         </Form>
      );
   }

   render(<Component />);

   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Falco'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Falco'));

   expect(onChangeValue).toHaveBeenCalled();
   expect(onChangeValue).toHaveBeenCalledWith(['Levi'], { name: 'Levi', value: 'Levi' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Falco'], { name: 'Falco', value: 'Falco' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Falco', 'Rainer'], { name: 'Rainer', value: 'Rainer' });
   expect(onChangeValue).toHaveBeenCalledWith(['Levi', 'Rainer'], { name: 'Falco', value: 'Falco' });
});

it('unform - check prop allowEmpty', async () => {
   const onChangeValue = jest.fn();

   function Component(): any {
      return (
         <Form onSubmit={() => {}}>
            <ButtonsGroup
               options={options}
               name="button"
               title="Characters"
               allowEmpty
               onSelect={(v) => {
                  onChangeValue(v);
               }}
            />
         </Form>
      );
   }

   render(<Component />);

   // Definir data
   fireEvent.click(screen.getByTestId('button-Levi'));
   fireEvent.click(screen.getByTestId('button-Rainer'));
   fireEvent.click(screen.getByTestId('button-Rainer'));

   await waitFor(() => expect(onChangeValue).toHaveBeenCalled());
   expect(onChangeValue).toHaveBeenCalledWith(null);

   expect(screen.getByTestId('button-Eren')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Levi')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Rainer')).toHaveAttribute('data-active', 'false');
   expect(screen.getByTestId('button-Falco')).toHaveAttribute('data-active', 'false');
});

it('unform - get value on onSubmit - initial value', () => {
   const onSubmit = jest.fn();

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

      return (
         <>
            <Form onSubmit={(v) => onSubmit(v)} initialData={{ buttons: 'Rainer' }} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} />
               <button
                  data-testid="submit"
                  title="submit"
                  onClick={() => {
                     formRef.current.submitForm();
                  }}
               />
            </Form>
         </>
      );
   }

   render(<Component />);

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

   expect(onSubmit).toHaveBeenCalled();
   expect(onSubmit).toHaveBeenCalledWith({
      buttons: 'Rainer',
   });
});

it('unform - get value on onSubmit - initial value with multiSelection', () => {
   const onSubmit = jest.fn();

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

      return (
         <>
            <Form onSubmit={(v) => onSubmit(v)} initialData={{ buttons: ['Rainer', 'Eren'] }} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} multiSelection />
               <button
                  data-testid="submit"
                  title="submit"
                  onClick={() => {
                     formRef.current.submitForm();
                  }}
               />
            </Form>
         </>
      );
   }

   render(<Component />);

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

   expect(onSubmit).toHaveBeenCalled();
   expect(onSubmit).toHaveBeenCalledWith({
      buttons: ['Rainer', 'Eren'],
   });
});

it('unform - get value on onSubmit - clear form', () => {
   const onSubmit = jest.fn();

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

      function clearForm(): void {
         formRef.current.reset();
      }

      return (
         <>
            <Form onSubmit={(v) => onSubmit(v)} initialData={{ buttons: 'Rainer' }} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} />
               <button
                  data-testid="submit"
                  title="submit"
                  onClick={() => {
                     formRef.current.submitForm();
                  }}
               />
               <button data-testid="clear" title="clear" onClick={clearForm} />
            </Form>
         </>
      );
   }

   render(<Component />);

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

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

it('unform - get value on onSubmit - set data', () => {
   const onSubmit = jest.fn();

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

      function setData(): void {
         formRef.current.setFieldValue('buttons', 'Falco');
      }

      return (
         <>
            <Form onSubmit={(v) => onSubmit(v)} ref={formRef}>
               <ButtonsGroup name="buttons" options={options} />
               <button data-testid="setData" title="setData" onClick={setData} />
               <button
                  data-testid="submit"
                  title="submit"
                  onClick={() => {
                     formRef.current.submitForm();
                  }}
               />
            </Form>
         </>
      );
   }

   render(<Component />);

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

   expect(onSubmit).toHaveBeenCalled();
   expect(onSubmit).toHaveBeenCalledWith({
      buttons: 'Falco',
   });
});
