import type {
  AnyFieldApi,
  AnyFormApi,
} from '@tanstack/react-form'
import type {
  BaseFieldProps,
} from '.'
import type { FieldState, FormRef, FormSchema, SetFieldsParam } from '@/app/components/base/form/types'
import { cn } from '@langgenius/dify-ui/cn'
import {
  useForm,
  useStore,
} from '@tanstack/react-form'
import {
  memo,
  useCallback,
  useImperativeHandle,
  useMemo,
  useState,
} from 'react'
import {
  useGetFormValues,
  useGetValidators,
} from '@/app/components/base/form/hooks'
import {

  FormItemValidateStatusEnum,

} from '@/app/components/base/form/types'
import {
  BaseField,
} from '.'

export type BaseFormProps = {
  formSchemas?: FormSchema[]
  defaultValues?: Record<string, any>
  formClassName?: string
  ref?: FormRef
  disabled?: boolean
  formFromProps?: AnyFormApi
  onChange?: (field: string, value: any) => void
  onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void
  preventDefaultSubmit?: boolean
} & Pick<BaseFieldProps, 'fieldClassName' | 'labelClassName' | 'inputContainerClassName' | 'inputClassName'>

const BaseForm = ({
  formSchemas = [],
  defaultValues,
  formClassName,
  fieldClassName,
  labelClassName,
  inputContainerClassName,
  inputClassName,
  ref,
  disabled,
  formFromProps,
  onChange,
  onSubmit,
  preventDefaultSubmit = false,
}: BaseFormProps) => {
  const initialDefaultValues = useMemo(() => {
    if (defaultValues)
      return defaultValues

    return formSchemas.reduce((acc, schema) => {
      if (schema.default)
        acc[schema.name] = schema.default
      return acc
    }, {} as Record<string, any>)
  }, [defaultValues])
  const formFromHook = useForm({
    defaultValues: initialDefaultValues,
  })
  const form: any = formFromProps || formFromHook
  const { getFormValues } = useGetFormValues(form, formSchemas)
  const { getValidators } = useGetValidators()

  const [fieldStates, setFieldStates] = useState<Record<string, FieldState>>({})

  const showOnValues = useStore(form.store, (s: any) => {
    const result: Record<string, any> = {}
    formSchemas.forEach((schema) => {
      const { show_on } = schema
      if (show_on?.length) {
        show_on.forEach((condition) => {
          result[condition.variable] = s.values[condition.variable]
        })
      }
    })
    return result
  })

  const setFields = useCallback((fields: SetFieldsParam[]) => {
    const newFieldStates: Record<string, FieldState> = { ...fieldStates }

    for (const field of fields) {
      const { name, value, errors, warnings, validateStatus, help } = field

      if (value !== undefined)
        form.setFieldValue(name, value)

      let finalValidateStatus = validateStatus
      if (!finalValidateStatus) {
        if (errors && errors.length > 0)
          finalValidateStatus = FormItemValidateStatusEnum.Error
        else if (warnings && warnings.length > 0)
          finalValidateStatus = FormItemValidateStatusEnum.Warning
      }

      newFieldStates[name] = {
        validateStatus: finalValidateStatus,
        help,
        errors,
        warnings,
      }
    }

    setFieldStates(newFieldStates)
  }, [form, fieldStates])

  useImperativeHandle(ref, () => {
    return {
      getForm() {
        return form
      },
      getFormValues: (option) => {
        return getFormValues(option)
      },
      setFields,
    }
  }, [form, getFormValues, setFields])

  const renderField = useCallback((field: AnyFieldApi) => {
    const formSchema = formSchemas?.find(schema => schema.name === field.name)

    if (formSchema) {
      return (
        <BaseField
          field={field}
          formSchema={formSchema}
          fieldClassName={fieldClassName ?? formSchema.fieldClassName}
          labelClassName={labelClassName ?? formSchema.labelClassName}
          inputContainerClassName={inputContainerClassName}
          inputClassName={inputClassName}
          disabled={disabled}
          onChange={onChange}
          fieldState={fieldStates[field.name]}
        />
      )
    }

    return null
  }, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange, fieldStates])

  const renderFieldWrapper = useCallback((formSchema: FormSchema) => {
    const validators = getValidators(formSchema)
    const {
      name,
      show_on = [],
    } = formSchema

    const show = show_on?.every((condition) => {
      const conditionValue = showOnValues[condition.variable]
      return conditionValue === condition.value
    })

    if (!show)
      return null

    return (
      <form.Field
        key={name}
        name={name}
        validators={validators}
      >
        {renderField}
      </form.Field>
    )
  }, [renderField, form, getValidators, showOnValues])

  if (!formSchemas?.length)
    return null

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    if (preventDefaultSubmit) {
      e.preventDefault()
      e.stopPropagation()
    }
    onSubmit?.(e)
  }

  return (
    <form
      className={cn(formClassName)}
      onSubmit={handleSubmit}
    >
      {formSchemas.map(renderFieldWrapper)}
    </form>
  )
}

export default memo(BaseForm)
