import { Fragment } from 'react';

type WithChildren = {
  id: string;
  children: any[];
  siblings: number;
};

interface TreeProps<T extends WithChildren> {
  items: T[];
  render: (a: TreeRenderProps<Omit<T, 'children' | 'siblings'>>) => JSX.Element;
  disabled?: (a: T) => boolean;
  toggle: (id: string) => void;
  isHidden: (id: string) => boolean;
  level?: number;
}

interface TreeRenderProps<T> {
  data: T;
  children: any[];
  siblings: number;
  isOpen: boolean;
  level: number;
  toggleTree?: () => void;
}

export const Tree = <T extends WithChildren>({
  items,
  render,
  disabled,
  toggle,
  isHidden,
  level = 0,
}: TreeProps<T>) => {
  return (
    <Fragment>
      {items.map((item) => {
        const { children, siblings, ...data } = item;
        const isOpen = !isHidden(item.id);
        const isDisabled =
          typeof disabled === 'function' ? disabled(item) : false;
        const hasChildren = item.children.length > 0;
        const renderProps: TreeRenderProps<Omit<T, 'children' | 'siblings'>> = {
          data,
          children,
          siblings,
          isOpen,
          level,
          toggleTree:
            hasChildren && !isDisabled ? () => toggle(item.id) : undefined,
        };
        const treeItem = render(renderProps);
        const treeChildren = hasChildren && !isDisabled && isOpen;
        if (treeItem) {
          return (
            <Fragment key={item.id}>
              {treeItem}
              {treeChildren && (
                <Tree
                  items={item.children}
                  render={render}
                  disabled={disabled}
                  toggle={toggle}
                  isHidden={isHidden}
                  level={level + 1}
                />
              )}
            </Fragment>
          );
        } else {
          return null;
        }
      })}
    </Fragment>
  );
};
