import { useAddItem, useErrorCount, useRemoveItem } from '@/components/ra-forms/LongForm/Provider';
import { useIsActive } from '@/components/ra-forms/LongForm/hooks';
import { IItem } from '@/components/ra-forms/LongForm/types';
import { getId } from '@/components/ra-forms/LongForm/utils';
import { Optional } from '@/types';
import { walkChildren } from '@/utils';
import { Box } from '@mui/material';
import _ from 'lodash';
import React, { Children, cloneElement, isValidElement, useEffect, useMemo } from 'react';
import { useFormState } from 'react-hook-form';

type IBaseItemProps = React.PropsWithChildren<Optional<IItem, 'id' | 'index'> & { sources: Array<string> }>;
type ITabProps = IBaseItemProps;
type IGroupProps = IBaseItemProps;

function BaseItem(props: IBaseItemProps) {
  const { errors } = useFormState();
  const countErrors = useErrorCount();
  const { label, icon, badge, index = 0, children } = props;
  const id = getId(props);
  const addItem = useAddItem();
  const removeItem = useRemoveItem();
  const visible = useIsActive(id);
  const sources: Array<string> = [];

  if (countErrors) {
    if (props.sources !== undefined) {
      sources.push(...props.sources);
    } else {
      walkChildren(children, (el) => {
        if (el?.props?.source !== undefined) {
          sources.push(el.props.source);
        }
      });
    }
  }

  const errorsCount = _.chain(sources)
    .uniq()
    .map((s) => _.get(errors, s))
    .reject((s) => s === undefined)
    .value().length;

  useEffect(() => {
    addItem({
      id: id,
      index: index,
      label: label,
      icon: icon,
      badge: badge,
      errors: errorsCount
    });
  }, [addItem, removeItem, label, icon, id, badge, index, errorsCount]);

  useEffect(() => {
    return () => {
      removeItem(id);
    };
  }, [id, removeItem]);

  /* All tabs are rendered (not only the one in focus), to allow validation
  on tabs not in focus. The tabs receive a `hidden` property, which they'll
  use to hide the tab using CSS if it's not the one in focus.
  See https://github.com/marmelab/react-admin/issues/1866 */
  return <Box display={visible ? 'unset' : 'none'}>{props.children}</Box>;
}

function Group(props: IGroupProps) {
  const filteredChildren = useBaseItemChildren(props);
  const groupId = getId(props);
  return (
    <BaseItem {...props} id={groupId}>
      {Children.map(filteredChildren, (Child) => {
        const childId = getId(Child.props);
        return cloneElement(Child, { id: `${groupId}.${childId}` });
      })}
    </BaseItem>
  );
}

function Tab(props: ITabProps) {
  return <BaseItem {...props} />;
}

function useBaseItemChildren(props: React.PropsWithChildren): Array<React.FunctionComponentElement<IBaseItemProps>> {
  const { children } = props;
  const result = useMemo<Array<React.FunctionComponentElement<IBaseItemProps>>>(
    () =>
      //@ts-ignore
      _.chain(Children.toArray(children))
        .filter((Child) => isValidElement(Child) && (Child?.type === Tab || Child?.type === Group))
        .map((Child: React.FunctionComponentElement<IBaseItemProps>, index) => cloneElement(Child, { index: index }))
        .value(),
    [children]
  );
  return result;
}

export { Group, Tab, useBaseItemChildren };
