import { screen, fireEvent, waitFor } from '@testing-library/react'
import { Formik } from 'formik'
import { renderWithContext } from '../tests/with-app-context.js'
import { EmailInput } from './email-input.js'

describe('Base Components: EmailInput', () => {
  const defaultRequiredErrorMessage = 'Required'
  const defaultInvalidErrorMessage = 'Please enter a valid email address'
  it('renders default errors', async () => {
    const testId = 'email-input-test-id'

    renderWithContext(
      <Formik
        initialValues={{
          test: '',
        }}
        onSubmit={() => {}}
      >
        <EmailInput name="test" data-testid={testId} />
      </Formik>
    )

    const input = screen.getByTestId(testId) as HTMLInputElement

    expect(screen.queryByText(defaultRequiredErrorMessage)).not.toBeInTheDocument()
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: 'abc' } })
    fireEvent.blur(input)

    await screen.findByText(defaultInvalidErrorMessage)

    fireEvent.change(input, { target: { value: 'abc@example.com' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.queryByText(defaultRequiredErrorMessage)).not.toBeInTheDocument()
    })
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: '' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.queryByText(defaultRequiredErrorMessage)).not.toBeInTheDocument()
    })
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()
  })

  it('renders required errors', async () => {
    const testId = 'email-input-test-id'

    renderWithContext(
      <Formik
        initialValues={{
          test: '',
        }}
        onSubmit={() => {}}
      >
        <EmailInput name="test" data-testid={testId} isRequired />
      </Formik>
    )

    const input = screen.getByTestId(testId) as HTMLInputElement

    expect(screen.queryByText(defaultRequiredErrorMessage)).not.toBeInTheDocument()
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: 'abc' } })
    fireEvent.blur(input)

    await screen.findByText(defaultInvalidErrorMessage)

    fireEvent.change(input, { target: { value: 'abc@example.com' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.queryByText(defaultRequiredErrorMessage)).not.toBeInTheDocument()
    })
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: '' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.getByText(defaultRequiredErrorMessage)).toBeInTheDocument()
    })
    expect(screen.queryByText(defaultInvalidErrorMessage)).not.toBeInTheDocument()
  })

  it('renders custom error messages', async () => {
    const testId = 'email-input-test-id'
    const testRequiredErrorMessage = 'test-is-required'
    const testInvalidErrorMessage = 'test-is-invalid'

    renderWithContext(
      <Formik
        initialValues={{
          test: '',
        }}
        onSubmit={() => {}}
      >
        <EmailInput
          name="test"
          data-testid={testId}
          isRequired
          requiredErrorMessage={testRequiredErrorMessage}
          invalidErrorMessage={testInvalidErrorMessage}
        />
      </Formik>
    )

    const input = screen.getByTestId(testId) as HTMLInputElement

    expect(screen.queryByText(testRequiredErrorMessage)).not.toBeInTheDocument()
    expect(screen.queryByText(testInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: 'abc' } })
    fireEvent.blur(input)

    await screen.findByText(testInvalidErrorMessage)

    fireEvent.change(input, { target: { value: 'abc@example.com' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.queryByText(testRequiredErrorMessage)).not.toBeInTheDocument()
    })
    expect(screen.queryByText(testInvalidErrorMessage)).not.toBeInTheDocument()

    fireEvent.change(input, { target: { value: '' } })
    fireEvent.blur(input)

    await waitFor(() => {
      expect(screen.getByText(testRequiredErrorMessage)).toBeInTheDocument()
    })
    expect(screen.queryByText(testInvalidErrorMessage)).not.toBeInTheDocument()
  })
})
