import React from 'react'
import { FormState, FieldState } from 'formstate-x'
import { renderHook } from '@testing-library/react-hooks'
import { create } from 'react-test-renderer'

import Form, { useFormstateX, ModalForm, DrawerForm } from './Form'

/* eslint-disable no-console */

describe('useFormstateX', () => {
  it('should work well with empty parameters', () => {
    function createState() {
      return new FormState({
        foo: new FieldState('foo'),
        bar: new FieldState('bar')
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, [])
    )

    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe('bar')
  })

  it('should work well with inline createFieldState', () => {
    const { result } = renderHook(
      props => useFormstateX(() => new FieldState(props.foo), [props.foo]),
      { initialProps: { foo: 3 } }
    )

    expect(result.current.value).toBe(3)
  })

  it('should work well with inline createFormState', () => {
    const { result } = renderHook(
      props => useFormstateX(() => new FormState({
        foo: new FieldState(props.foo),
        bar: new FieldState(props.bar)
      }), [props.foo, props.bar]),
      { initialProps: { foo: 3, bar: 'bar' } }
    )

    expect(result.current.value.foo).toBe(3)
    expect(result.current.value.bar).toBe('bar')
  })

  it('should work well with default parameters', () => {
    function createState(foo = 'foo') {
      return new FormState({
        foo: new FieldState(foo)
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, [])
    )

    expect(result.current.value.foo).toBe('foo')
  })

  it('should work well with partial default parameters', () => {
    function createState(foo: string, bar = 3) {
      return new FormState({
        foo: new FieldState(foo),
        bar: new FieldState(bar)
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, ['foo'])
    )

    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe(3)
  })

  it('should work well with optional parameters', () => {
    function createState(foo?: string) {
      return new FormState({
        foo: new FieldState(foo != null ? foo : 'foo')
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, [])
    )

    expect(result.current.value.foo).toBe('foo')
  })

  it('should work well with partial optional parameters', () => {
    function createState(foo: string, bar?: number) {
      return new FormState({
        foo: new FieldState(foo),
        bar: new FieldState(bar == null ? 3 : bar)
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, ['foo'])
    )

    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe(3)
  })

  it('should work well with partial optional parameters fully filled', () => {
    function createState(foo: string, bar?: number) {
      return new FormState({
        foo: new FieldState(foo),
        bar: new FieldState(bar == null ? 3 : bar)
      })
    }

    const { result } = renderHook(
      () => useFormstateX(createState, ['foo', 5])
    )

    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe(5)
  })

  it('should work well with auto recreate state', () => {
    function createState(foo: string, bar: number) {
      return new FormState({
        foo: new FieldState(foo),
        bar: new FieldState(bar)
      })
    }

    const { result, rerender } = renderHook(
      props => useFormstateX(createState, [props.foo, props.bar]),
      { initialProps: { foo: 'foo', bar: 3 } }
    )

    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe(3)

    const result1 = result.current

    rerender()
    expect(result.current).toBe(result1)
    expect(result.current.value.foo).toBe('foo')
    expect(result.current.value.bar).toBe(3)

    rerender({ foo: 'foo2', bar: 4 })
    expect(result.current).not.toBe(result1)
    expect(result.current.value.foo).toBe('foo2')
    expect(result.current.value.bar).toBe(4)
  })
})

describe('Form related component', () => {
  function createState() {
    return new FormState({
      foo: new FieldState('foo'),
      bar: new FieldState('bar')
    })
  }

  type Value = {
    foo: string
    bar: string
  }

  const state = createState()

  function handleSubmit(value: Value) {
    console.log('submit with', value)
  }

  function handleCancel() {
    console.log('cancelled')
  }

  describe('Form', () => {
    it('should render well', async () => {
      const renderer = create(
        <Form state={state} onSubmit={handleSubmit} onCancel={handleCancel}>
          content
        </Form>
      )
      await new Promise<void>(resolve => {
        setTimeout(() => {
          resolve()
        }, 500)
      })
      expect(renderer.toJSON()).toMatchSnapshot()
    })
  })

  describe('ModalForm', () => {
    it('should render well', async () => {
      const renderer = create(
        <ModalForm
          title="title"
          visible
          state={state}
          onSubmit={handleSubmit}
          onCancel={handleCancel}
        >
          content
        </ModalForm>
      )
      await new Promise<void>(resolve => {
        setTimeout(() => {
          resolve()
        }, 500)
      })
      expect(renderer.toJSON()).toMatchSnapshot()
    })
  })

  describe('DrawerForm', () => {
    it('should render well', async () => {
      const renderer = create(
        <DrawerForm
          title="title"
          visible
          state={state}
          onSubmit={handleSubmit}
          onCancel={handleCancel}
        >
          content
        </DrawerForm>
      )
      await new Promise<void>(resolve => {
        setTimeout(() => {
          resolve()
        }, 500)
      })
      expect(renderer.toJSON()).toMatchSnapshot()
    })
  })
})
