import { useState, FormEvent, ChangeEvent, SyntheticEvent } from 'react'

import { InputProps } from './types'
import { getValue, setValue } from '../lib/nestedValues'

interface FormHandler<ValueObject> {
  values: ValueObject,
  errors: Dict,
  isSubmitting: boolean,
  setValues: any,
  inputProps: (name: string) => InputProps,
  handleChange: (event: ChangeEvent) => void,
  handleBlur: (event: SyntheticEvent) => void,
  handleSubmit: (event: FormEvent, data: any) => void
}

/**
 * Provides hooks for forms.
 *
 * @remarks
 * You can use it like this
 * ```ts
 * const {inputParams, formParams} = useFormHandler()
 * ```
 *
 * @param initialValues - Initial values of inputs
 * @param validate - validation function for the form
 * @returns hooks to handle the form
 */
export function useFormHandler<ValueObject>(
  initialValues: ValueObject,
  validate?: (values: Dict) => {},
  submit?: (data: any) => {}
): FormHandler<ValueObject> {

  const [values, setValues] = useState(initialValues)
  const [errors, setErrors] = useState({})
  const [isSubmitting, setIsSubmitting] = useState(false)

  function handleChange(event: ChangeEvent): void {
    const target = (event.target as HTMLInputElement)
    console.log('change', values, target.name, target.value)
    setValues(previousValues => {
      return setValue(previousValues, target.name, target.value)
    })
  }

  function handleBlur(event: SyntheticEvent): void {
    if (validate) {
      const validationErrors = validate(values)
      setErrors(validationErrors)
    }
  }

  function handleSubmit(event: FormEvent, data: any): void {
    event.preventDefault()
    if (submit) {
      submit(data)
    }
  }

  // users[3].name
  function inputProps(name: string, options?: any): any {
    const path = (options && options.subtree) ? `${options.subtree}.${name}` : name

    return {
      id: path,
      name: path,
      value: getValue(values, path),
      onChange: handleChange,
      onBlur: handleBlur,
      placeholder: options && options.label || name,
      label: options && options.label || name
    }
  }

  return {
    values,
    errors,
    isSubmitting,
    setValues,
    inputProps,
    handleChange,
    handleBlur,
    handleSubmit
  }
}
