import { renderWithClient } from '../../../utils/test/react-query';
import { MemoryRouter } from 'react-router-dom';
import RegisterForm from '../register-form';
import { mockedAuthKey, mockedMobile, mockedRegisterUserData } from '../../../mocks/data';
import * as userAPI from '../../../api/user';
import * as introducerAPI from '../../../api/introducer';
import * as authAPI from '../../../api/auth';
import userEvent from '@testing-library/user-event';
import { waitFor } from '@testing-library/react';

describe('RegisterForm component', () => {
  const data = mockedRegisterUserData;
  const { iban, ...userData } = data;
  const { mobile, password } = userData;

  const mockedHandleReset = jest.fn();
  const user = userEvent.setup();

  afterEach(() => {
    jest.resetAllMocks();
    jest.restoreAllMocks();
  });

  test('successful render component', async () => {
    const result = renderWithClient(
      <RegisterForm mobile={mockedMobile} authKey={mockedAuthKey} reset={mockedHandleReset} />,
      MemoryRouter
    );
    expect(await result.findByTestId('register-form')).toBeInTheDocument();
    expect(await result.findByText(mockedMobile)).toBeInTheDocument();
    expect(await result.findByText('تایید')).toBeInTheDocument();
    mockedHandleReset.mockRestore();
  });

  test('call registerUser functions with correct arguments', async () => {
    const createUserSpy = jest.spyOn(userAPI, 'createUser');
    const createIntroducerSpy = jest.spyOn(introducerAPI, 'createIntroducer');
    const fetchTokenSpy = jest.spyOn(authAPI, 'fetchToken');

    const result = renderWithClient(
      <RegisterForm mobile={mockedMobile} authKey={mockedAuthKey} reset={mockedHandleReset} />,
      MemoryRouter
    );
    await user.type(result.getByTestId('firstName-field'), data.firstName);
    await user.type(result.getByTestId('lastName-field'), data.lastName);
    await user.selectOptions(await result.getByTestId('gender-field'), data.gender);
    await user.type(result.getByTestId('password-field'), data.password);
    await user.type(result.getByTestId('rePassword-field'), data.password);
    if (data.iban) await user.type(result.getByTestId('iban-field'), data.iban);
    await user.click(await result.getByTestId('termsAndConditions-field'));
    await user.click(await result.getByRole('button', { name: /تایید/i }));

    await waitFor(() => expect(createUserSpy).toHaveBeenCalledWith(userData));
    await waitFor(() => expect(createIntroducerSpy).toHaveBeenCalledWith({ iban }));
    await waitFor(() => expect(fetchTokenSpy).toHaveBeenCalledWith({ mobile, password }));
    await waitFor(() => expect(mockedHandleReset).toHaveBeenCalled());
  });

  test('form errors', async () => {
    const result = renderWithClient(
      <RegisterForm mobile={mockedMobile} authKey={mockedAuthKey} reset={mockedHandleReset} />,
      MemoryRouter
    );
    user.click(await result.getByRole('button', { name: /تایید/i }));

    expect(mockedHandleReset).not.toHaveBeenCalled();

    for (const fieldName of [
      'firstName',
      'lastName',
      'gender',
      'password',
      'rePassword',
      'termsAndConditions'
    ]) {
      await waitFor(() =>
        expect(result.getByTestId(`${fieldName}-field-error`)).toHaveTextContent(
          fieldName === 'termsAndConditions'
            ? 'پذیرفتن شرایط و قوانین الزامی است.'
            : 'وارد کردن این فیلد الزامی است.'
        )
      );
    }

    await user.selectOptions(await result.getByTestId('gender-field'), '');
    await user.tab();
    expect(await result.getByTestId('gender-field-error')).toHaveTextContent(
      'وارد کردن این فیلد الزامی است.'
    );
  });

  test('form verification', async () => {
    const createUserSpy = jest.spyOn(userAPI, 'createUser');
    const createIntroducerSpy = jest.spyOn(introducerAPI, 'createIntroducer');
    const fetchTokenSpy = jest.spyOn(authAPI, 'fetchToken');

    const result = renderWithClient(
      <RegisterForm mobile={mockedMobile} authKey={mockedAuthKey} reset={mockedHandleReset} />,
      MemoryRouter
    );
    await user.type(result.getByTestId('firstName-field'), data.firstName);
    await user.type(result.getByTestId('lastName-field'), data.lastName);
    await user.selectOptions(result.getByTestId('gender-field'), data.gender);
    await user.type(result.getByTestId('password-field'), data.password);
    await user.type(result.getByTestId('rePassword-field'), data.password.substring(1));
    if (data.iban) await user.type(result.getByTestId('iban-field'), data.iban);
    await user.click(result.getByTestId('termsAndConditions-field'));
    await user.click(result.getByRole('button', { name: /تایید/i }));

    await waitFor(() =>
      expect(result.getByTestId(`rePassword-field-error`)).toHaveTextContent(
        'رمزهای عبور غیر یکسان هستند.'
      )
    );

    await waitFor(() => expect(createUserSpy).not.toHaveBeenCalled());
    await waitFor(() => expect(createIntroducerSpy).not.toHaveBeenCalled());
    await waitFor(() => expect(fetchTokenSpy).not.toHaveBeenCalled());
    await waitFor(() => expect(mockedHandleReset).not.toHaveBeenCalled());
  });
});
