import React, {
  forwardRef,
  RefObject,
  useEffect,
  useImperativeHandle,
  useMemo,
  type MutableRefObject,
  type ReactElement,
  type ReactNode,
  type Ref,
} from 'react'
import { useFormContext, useFormState, type FieldValues, type UseFormReturn } from 'react-hook-form'
import { v4 as uuid } from 'uuid'
import {
  ActionIcon,
  Group,
  List,
  LoadingOverlay,
  Paper,
  Spoiler,
  Stack,
  Title,
  Tooltip,
  Transition,
} from '@mantine/core'
import { IconChevronsLeft, IconChevronsRight } from '@tabler/icons-react'
import { useUncontrolled } from '@mantine/hooks'
import useRemote from '../hooks/useRemote'
import { useStore } from '../store/SuperForm.store'
import classes from '../styles/Form.module.css'
import { Form } from './Form'
import { FormLayout } from './FormLayout'
import type { GridRef, SuperFormRef } from '../types'

const SuperFormLayout = <T extends FieldValues>(
  {
    children,
    gridRef,
  }: {
    children: React.ReactNode | ((props: UseFormReturn<T, any, undefined>) => React.ReactNode)
    gridRef?: MutableRefObject<GridRef<any> | null>
  },
  ref
) => {
  // Component store State
  const {
    layoutProps,
    meta,
    nested,
    onBeforeSubmit,
    onCancel,
    onLayoutMounted,
    onLayoutUnMounted,
    onResetForm,
    onSubmit,
    primeData,
    request,
    tableName,
    value,
  } = useStore((state) => ({
    extraButtons: state.extraButtons,
    layoutProps: state.layoutProps,
    meta: state.meta,
    nested: state.nested,
    onBeforeSubmit: state.onBeforeSubmit,
    onCancel: state.onCancel,
    onLayoutMounted: state.onLayoutMounted,
    onLayoutUnMounted: state.onLayoutUnMounted,
    onResetForm: state.onResetForm,
    onSubmit: state.onSubmit,
    primeData: state.primeData,
    request: state.request,
    tableName: state.remote?.tableName,
    value: state.value,
  }))

  const [_opened, _setOpened] = useUncontrolled({
    value: layoutProps?.bodyRightSection?.opened,
    defaultValue: false,
    onChange: layoutProps?.bodyRightSection?.setOpened,
  })

  // Component Hooks
  const form = useFormContext<T, any, undefined>()
  const formState = useFormState({ control: form.control })

  const { isFetching, mutateAsync, error, queryKey } = useRemote(gridRef)

  // Component variables
  const formUID = useMemo(() => {
    return meta?.id ?? uuid()
  }, [])

  const requestString = request?.charAt(0).toUpperCase() + request?.slice(1)

  const renderRightSection = (
    <>
      <Tooltip label={`${_opened ? 'Close' : 'Open'} Right Section`} withArrow>
        <ActionIcon
          style={{
            position: 'absolute',
            right: 12,
            zIndex: 5,

            display: layoutProps?.bodyRightSection?.hideToggleButton ? 'none' : 'block',
          }}
          variant='filled'
          size='sm'
          onClick={() => _setOpened(!_opened)}
          radius='6'
          m={2}
        >
          {_opened ? <IconChevronsRight /> : <IconChevronsLeft />}
        </ActionIcon>
      </Tooltip>
      <Group wrap='nowrap' h='100%' align='flex-start' gap={2} w={'100%'}>
        <Stack gap={0} h='100%' style={{ flex: 1 }}>
          {typeof children === 'function' ? children({ ...form }) : children}
        </Stack>
        <Transition transition='slide-left' mounted={_opened}>
          {(transitionStyles) => (
            <Paper
              style={transitionStyles}
              h='100%'
              w={layoutProps?.bodyRightSection?.w}
              shadow='xs'
              radius='xs'
              mr='xs'
              mt='xs'
              ml={0}
              {...layoutProps?.bodyRightSection?.paperProps}
            >
              {layoutProps?.bodyRightSection?.render?.({
                form,
                formValue: form.getValues(),
                isFetching,
                opened: _opened,
                queryKey,
                setOpened: _setOpened,
              })}
            </Paper>
          )}
        </Transition>
      </Group>
    </>
  )

  // Component Callback Functions
  const onFormSubmit = async (data: T | any, closeForm: boolean = true) => {
    const res: any =
      typeof onBeforeSubmit === 'function'
        ? await mutateAsync?.(await onBeforeSubmit(data, request, form))
        : await mutateAsync?.(data)

    if ((tableName?.length ?? 0) > 0) {
      if (res?.ok || (res?.status >= 200 && res?.status < 300)) {
        onSubmit?.(res?.data, request, data, form, closeForm)
      } else {
        form.setError('root', {
          message: res.status === 401 ? 'Username or password is incorrect' : res?.error,
        })
      }
    } else {
      onSubmit?.(data, request, data, form, closeForm)
    }
  }

  // Component use Effects
  useEffect(() => {
    if (request === 'insert') {
      if (onResetForm) {
        onResetForm(primeData, form).then((resetData) => {
          form.reset(resetData)
        })
      } else {
        form.reset(primeData)
      }
    } else if ((request === 'change' || request === 'delete') && (tableName?.length ?? 0) === 0) {
      if (onResetForm) {
        onResetForm(value, form).then((resetData) => {
          form.reset(resetData)
        })
      } else {
        form.reset(value)
      }
    }
    onLayoutMounted?.()
    return onLayoutUnMounted
  }, [
    request,
    primeData,
    tableName,
    value,
    form.reset,
    onResetForm,
    onLayoutMounted,
    onLayoutUnMounted,
  ])

  useEffect(() => {
    if (
      (Object.keys(formState.errors)?.length > 0 || error) &&
      _opened === false &&
      layoutProps?.showErrorList !== false
    ) {
      _setOpened(true)
    }
  }, [Object.keys(formState.errors)?.length > 0, error, layoutProps?.showErrorList])

  useImperativeHandle<SuperFormRef<T>, SuperFormRef<T>>(ref, () => ({
    form,
    mutation: { isFetching, mutateAsync, error },
    submit: (closeForm: boolean = true, afterSubmit?: (data: T | any) => void) => {
      return form.handleSubmit(async (data: T | any) => {
        await onFormSubmit(data, closeForm)
        afterSubmit?.(data)
      })()
    },
    queryKey,
    getFormState: () => formState,
  }))

  return (
    <form
      name={formUID}
      onSubmit={(e) => {
        e.stopPropagation()
        e.preventDefault()
        form.handleSubmit((data: T | any) => {
          onFormSubmit(data)
        })(e)
      }}
      style={{ height: '100%' }}
      className={request === 'view' ? classes.disabled : ''}
    >
      {/* <LoadingOverlay
        visible={isFetching}
        overlayProps={{
          backgroundOpacity: 0.5,
        }}
      /> */}
      {layoutProps?.noLayout ? (
        typeof layoutProps?.bodyRightSection?.render === 'function' ? (
          renderRightSection
        ) : typeof children === 'function' ? (
          <>
            <LoadingOverlay
              visible={isFetching}
              overlayProps={{
                backgroundOpacity: 0.5,
              }}
            />
            {children({ ...form })}
          </>
        ) : (
          <>
            <LoadingOverlay
              visible={isFetching}
              overlayProps={{
                backgroundOpacity: 0.5,
              }}
            />
            {children}
          </>
        )
      ) : (
        <FormLayout
          dirty={formState.isDirty}
          loading={isFetching}
          onCancel={() => onCancel?.(request)}
          onSubmit={form.handleSubmit((data: T | any) => {
            onFormSubmit(data)
          })}
          request={request}
          modal={false}
          nested={nested}
        >
          {!layoutProps?.noHeader && (
            <Form.Section
              type='header'
              title={`${layoutProps?.title || requestString}`}
              rightSection={layoutProps?.rightSection}
            />
          )}
          {(Object.keys(formState.errors)?.length > 0 || error) && (
            <Form.Section
              className={classes.sticky}
              buttonTitles={layoutProps?.buttonTitles}
              type='error'
            >
              <Title order={6} size='sm' c='red'>
                {(error?.message?.length ?? 0) > 0
                  ? 'Server Error'
                  : 'Required information is incomplete*'}
              </Title>
              {(error as any)?.response?.data?.msg ||
                (error as any)?.response?.data?._error ||
                error?.message}
              {layoutProps?.showErrorList !== false && (
                <Spoiler maxHeight={50} showLabel='Show more' hideLabel='Hide'>
                  <List
                    size='xs'
                    style={{
                      color: 'light-dark(var(--mantine-color-dark-7), var(--mantine-color-gray-2))',
                    }}
                  >
                    {getErrorMessages(formState.errors)}
                  </List>
                </Spoiler>
              )}
            </Form.Section>
          )}
          {typeof layoutProps?.bodyRightSection?.render === 'function' ? (
            <Form.Section type='body' {...layoutProps?.bodySectionProps}>
              {renderRightSection}
            </Form.Section>
          ) : (
            <Form.Section type='body' {...layoutProps?.bodySectionProps}>
              {typeof children === 'function' ? children({ ...form }) : children}
            </Form.Section>
          )}
          {!layoutProps?.noFooter && (
            <Form.Section
              className={classes.sticky}
              buttonTitles={layoutProps?.buttonTitles}
              type='footer'
              {...(typeof layoutProps?.footerSectionProps === 'function'
                ? layoutProps?.footerSectionProps(ref as RefObject<SuperFormRef<T>>)
                : layoutProps?.footerSectionProps)}
            >
              {typeof layoutProps?.extraButtons === 'function'
                ? layoutProps?.extraButtons(form)
                : layoutProps?.extraButtons}
            </Form.Section>
          )}
        </FormLayout>
      )}
    </form>
  )
}

const getErrorMessages = (errors: any): ReactNode | null => {
  return Object.keys(errors ?? {}).map((key) => {
    if (typeof errors[key] === 'object' && key !== 'ref') {
      return getErrorMessages(errors[key])
    }
    if (key !== 'message') {
      return null
    }

    return <List.Item key={key}>{errors[key]}</List.Item>
  })
}

const FRSuperFormLayout = forwardRef(SuperFormLayout) as <T extends FieldValues>(
  props: {
    children: React.ReactNode | ((props: UseFormReturn<T, any, undefined>) => React.ReactNode)
    gridRef?: MutableRefObject<GridRef | null>
  } & {
    ref?: Ref<SuperFormRef<T>>
  }
) => ReactElement

export default FRSuperFormLayout
