import React, { useCallback, useEffect, useRef, useState } from 'react'
import { IState, ValueOf } from 'formstate-x'
import {
  Form as BaseForm, FormProps as BaseFormProps,
  ModalForm as BaseModalForm, ModalFormProps as BaseModalFormProps,
  DrawerForm as BaseDrawerForm, DrawerFormProps as BaseDrawerFormProps
} from 'react-icecream'

export type SubmitHandler<V> = (value: V) => (void | Promise<void>)

export type WithState<
  BaseProps extends { onSubmit?: SubmitHandler<void> },
  S extends IState<V>,
  V = ValueOf<S>
> = (
  Omit<BaseProps, 'onSubmit'>
  & {
    /** 表单状态，`FormState` 实例 */
    state: S
    /** 提交行为的回调函数，表单校验通过才会触发回调 */
    onSubmit?: SubmitHandler<V>
  }
)

function useValidatedSubmit<V>(onSubmit: SubmitHandler<V> | undefined, state: IState<V>) {
  return useCallback(async () => {
    const validated = await state.validate()
    if (validated.hasError) return
    return onSubmit?.(validated.value)
  }, [state, onSubmit])
}

export type Props<S extends IState<V>, V = ValueOf<S>> = WithState<BaseFormProps, S, V>

export default function Form<S extends IState<V>, V = ValueOf<S>>({
  state,
  onSubmit,
  ...formProps
}: Props<S, V>) {
  const handleSubmit = useValidatedSubmit(onSubmit, state)
  return <BaseForm {...formProps} onSubmit={handleSubmit} />
}

export type ModalFormProps<S extends IState<V>, V = ValueOf<S>> = WithState<BaseModalFormProps, S, V>

export function ModalForm<S extends IState<V>, V = ValueOf<S>>({
  state,
  onSubmit,
  ...modalFormProps
}: ModalFormProps<S, V>) {
  const handleSubmit = useValidatedSubmit(onSubmit, state)
  return <BaseModalForm {...modalFormProps} onSubmit={handleSubmit} />
}

export type DrawerFormProps<S extends IState<V>, V = ValueOf<S>> = WithState<BaseDrawerFormProps, S, V>

export function DrawerForm<S extends IState<V>, V = ValueOf<S>>({
  state,
  onSubmit,
  ...drawerFormProps
}: DrawerFormProps<S, V>) {
  const handleSubmit = useValidatedSubmit(onSubmit, state)
  return <BaseDrawerForm {...drawerFormProps} onSubmit={handleSubmit} />
}

/**
 * 在 React 组件中构造 `FormState` / `FieldState` 实例的 hooks
 *
 * 使用姿势：
 * function createState(foo: Foo, bar?: Bar) {
 *  return new FormState({
 *    foo: new FieldState(foo)
 *    bar: new FieldState(bar)
 *  })
 * }
 *
 * function Comp({ foo, bar }) {
 *  const state = useFormstateX(createState, [foo, bar])
 * }
 * */

export function useFormstateX<D extends readonly any[], R extends IState, Fn extends(...args: D) => R>(
  createState: Fn,
  parameters: [...D]) {
  const [state, setState] = useState(() => createState(...parameters))

  const isFirstRef = useRef(true)

  useEffect(() => {
    // 跳过第一次 render 后的 effect，避免 state 的重复构造与替换；
    // 此时 createState 未发生变更，render 时所构造的 state 依然可用
    if (isFirstRef.current) {
      isFirstRef.current = false
      return
    }
    setState(createState(...parameters))
  }, parameters) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => state.dispose, [state])

  return state as ReturnType<Fn>
}
