import { Field } from './Field';
import { AddTableRow } from './AddTableRow';
import { TableCreateButton } from './TableCreateButton';
import { EditButton } from './EditButton';
import { ActionsMenu } from '@/components/ActionsMenu';
import { TableFormIteratorContext } from '@/components/ra-forms/TableForm/TableFormIteratorContext';
import { TableFormIteratorItem } from '@/components/ra-forms/TableForm/TableFormIteratorItem';
import {
  Paper,
  SxProps,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Typography
} from '@mui/material';
import { styled, useTheme } from '@mui/material/styles';
import { FormDataConsumer, RaRecord, useTranslate, useTranslateLabel } from 'ra-core';
import * as React from 'react';
import { Children, ReactElement, ReactNode, useCallback, useMemo, useRef } from 'react';
import { useArrayInput } from 'react-admin';
import { UseFieldArrayReturn, useFormContext } from 'react-hook-form';
import { Tooltip } from '@/components/@extended';
import { DeleteWithConfirmButton } from '@/components/ra-buttons';

/**
 * How to use TableFormIterator:
 *
 * @example
 *  <Grid container>
 *    <Grid item xs={12}>
 *       <ArrayInput label={false} source="descriptor.fields">
 *           <TableFormIterator label="Property catalog">
 *               <TextInput label="Name" source={'name'} validate={required()} />
 *               <TextInput label="Denomination" source={'denomination'} helperText={false} />
 *               <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
 *               {...}
 *            </TableFormIterator>
 *       </ArrayInput>
 *     </Grid>
 *   </Grid>
 */
function RawTableFormIterator(props: TableFormIteratorProps): ReactElement | null {
  const {
    children,
    resource,
    source,
    label,
    disableAdd = false,
    disableRemove = false,
    enableClearAll = false,
    className,
    empty,
    template = {},
    addButton = <AddTableRow />,
    inset,
    sx
  } = props;
  const { fields, remove, replace, append } = useArrayInput(props);
  const { resetField } = useFormContext();
  const theme = useTheme();
  const translate = useTranslate();
  const initialDefaultValue = useRef(template || {});

  const translateLabel = useTranslateLabel();
  const removeField = useCallback(
    (index: number) => {
      remove(index);
    },
    [remove]
  );

  if (fields.length > 0) {
    const { ...rest } = fields[0];
    initialDefaultValue.current = rest;
    // @ts-ignore
    for (const k in initialDefaultValue.current) initialDefaultValue.current[k] = null;
  }

  const addField = useCallback(
    (item: any = undefined) => {
      let defaultValue = item;
      if (item == null) {
        defaultValue = initialDefaultValue.current;
        if (
          Children.count(children) === 1 &&
          React.isValidElement(Children.only(children)) &&
          // @ts-ignore
          !Children.only(children).props.source &&
          // Make sure it's not a FormDataConsumer
          // @ts-ignore
          Children.map(children, (input) => React.isValidElement(input) && input.type !== FormDataConsumer).some(
            Boolean
          )
        ) {
          // ArrayInput used for an array of scalar values
          // (e.g. tags: ['foo', 'bar'])
          defaultValue = '';
        } else {
          // ArrayInput used for an array of objects
          // (e.g. authors: [{ firstName: 'John', lastName: 'Doe' }, { firstName: 'Jane', lastName: 'Doe' }])
          defaultValue = defaultValue || ({} as Record<string, unknown>);
          Children.forEach(children, (input) => {
            if (React.isValidElement(input) && input.type !== FormDataConsumer && input.props.source) {
              defaultValue[input.props.source] = input.props.defaultValue ?? null;
            }
          });
        }
      }
      const newField = { ...defaultValue, ...template };
      append(newField);
      // Make sure the newly added inputs are not considered dirty by react-hook-form
      resetField(`${source}.${fields.length}`, { defaultValue });
    },
    [append, children, resetField, source, fields.length, template]
  );

  // add field and call the onClick event of the button passed as addButton prop
  function handleAddButtonClick(originalOnClickHandler: any) {
    return (event: MouseEvent) => {
      addField();
      if (originalOnClickHandler) {
        originalOnClickHandler(event);
      }
    };
  }

  const handleArrayClear = useCallback(() => {
    replace([]);
  }, [replace]);

  const context = useMemo(
    () => ({
      total: fields.length,
      remove: removeField,
      source
    }),
    [fields.length, removeField, source]
  );

  // @ts-ignore
  const tableBorderColor = theme.palette.mode === 'dark' ? theme.palette.grey.A400 : theme.palette.grey.A800;
  const showClearAllButton = fields.length > 0 && enableClearAll;
  const basicStyles =
    inset === true
      ? {
          borderRadius: 0,
          borderBottom: `1px solid ${theme.palette.divider}`
        }
      : {
          mt: 2,
          border: `1px solid ${tableBorderColor}`,
          //@ts-ignore
          boxShadow: theme.customShadows.z0
        };

  return fields ? (
    // @ts-ignore
    <TableFormIteratorContext.Provider value={context}>
      <div className={className}>
        {React.cloneElement(addButton, {
          label,
          source,
          disableAdd,
          template,
          onClick:
            addButton?.type !== TableCreateButton
              ? handleAddButtonClick((props?.addButton as any)?.props?.onClick)
              : undefined,
          inset
        })}

        <TableContainer
          component={Paper}
          sx={{
            ...basicStyles,
            //@ts-ignore
            boxShadow: theme.customShadows.z0,
            ...sx
          }}
        >
          <Table size="small">
            <TableHead sx={inset === true ? {} : { borderTop: 0, borderBottom: `1px solid ${tableBorderColor}` }}>
              <TableRow>
                {/** @ts-ignore */}
                {Children.map(children, (input: ReactElement, index) => {
                  if (input.type === EditButton) {
                    return null;
                  }
                  if (!React.isValidElement<any>(input)) {
                    return null;
                  }

                  const columnText = (
                    <Typography display={'flex'} variant="subtitle1" color="text.primary" textTransform="none">
                      {translateLabel({ ...input.props, resource })}
                    </Typography>
                  );

                  return (
                    <TableCell key={index}>
                      {input.props.title ? (
                        // @ts-ignore
                        <Tooltip title={input.props.title} arrow>
                          {columnText}
                        </Tooltip>
                      ) : (
                        columnText
                      )}
                    </TableCell>
                  );
                })}

                {!disableRemove && showClearAllButton ? (
                  <TableCell key="actions" sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
                    <ActionsMenu horizontal>
                      <DeleteWithConfirmButton
                        onConfirm={handleArrayClear}
                        confirmContent={translate('ra.message.clear_array_input')}
                      />
                    </ActionsMenu>
                  </TableCell>
                ) : (
                  <TableCell key="empty_actions" />
                )}
              </TableRow>
            </TableHead>

            <TableBody>
              {fields.length === 0 && (
                <TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
                  <TableCell
                    colSpan={
                      Children.count(children) -
                      Children.toArray(children).filter(
                        (child) => React.isValidElement(child) && child.type === EditButton
                      ).length +
                      (disableRemove ? 0 : 1)
                    }
                    align="center"
                  >
                    {empty ? (
                      empty
                    ) : (
                      <Typography variant="body1" color="text.secondary">
                        -
                      </Typography>
                    )}
                  </TableCell>
                </TableRow>
              )}

              {fields.length > 0 &&
                fields.map((member: any, index: number) => (
                  <TableRow sx={{ borderBottom: 0 }} key={member.id}>
                    <TableFormIteratorItem
                      disableRemove={disableRemove}
                      index={index}
                      member={`${source}.${index}`}
                      onRemoveField={removeField}
                      record={(fields && fields[index]) || {}}
                      // @ts-ignore
                      resource={resource}
                      // @ts-ignore
                      source={source}
                    >
                      {children}
                    </TableFormIteratorItem>
                  </TableRow>
                ))}
            </TableBody>
          </Table>
        </TableContainer>
      </div>
    </TableFormIteratorContext.Provider>
  ) : null;
}

const TableFormIterator = styled(RawTableFormIterator, { slot: 'Root' })(({ theme }) => ({
  '& > div.MuiPaper-root': {
    overflowX: 'auto',
    // this decision has been made with Roberto in order to avoid Marco gets angry
    [theme.breakpoints.down('sm')]: {
      width: `calc(100vw - ${theme.spacing(8.25)})`
    }
  },
  [theme.breakpoints.down('sm')]: {
    '& .MuiTableRow-root:last-child': {
      borderBottom: 0
    }
  }
}));

interface TableFormIteratorProps extends Partial<UseFieldArrayReturn> {
  addButton?: ReactElement;
  children?: ReactNode;
  className?: string;
  disableAdd?: boolean;
  disableRemove?: boolean;
  enableClearAll?: boolean;
  /**
   * Adding the template prop will allow to set a default value for the new row.
   *
   * @example
   *  <Grid container>
   *    <Grid item xs={12}>
   *       <ArrayInput label={false} source="descriptor.fields">
   *           <TableFormIterator template={{ property: 'FIELD' }} label="Property catalog">
   *               <TextInput label="Name" source={'name'} validate={required()} />
   *               <TextInput source={'denomination'} helperText={false} />
   *               <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *            </TableFormIterator>
   *       </ArrayInput>
   *     </Grid>
   *   </Grid>
   */
  template?: object;
  record?: RaRecord;
  label?: string | boolean;
  resource?: string;
  source?: string;
  sx?: SxProps;
  empty: ReactNode | string;
  inset?: boolean;
}

type ITableForm = typeof TableFormIterator & {
  /**
   * TableFormiterator allows to insert an addButton which will render the default btn and open a dialog with the form fields passed as children.
   *
   * @example
   *  <Grid container>
   *    <Grid item xs={12}>
   *       <ArrayInput label={false} source="descriptor.fields">
   *           <TableFormIterator
   *              addButton={<TableFormIterator.CreateButton>
   *                <TextInput label="Name" source={'name'} validate={required()} />
   *                <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *                </TableFormIterator.CreateButton>}
   *              label="Property catalog">
   *               <TextInput label="Name" source={'name'} validate={required()} />
   *               <TextInput label="Denomination" source={'denomination'} helperText={false} />
   *               <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *            </TableFormIterator>
   *       </ArrayInput>
   *     </Grid>
   *   </Grid>
   */
  CreateButton: typeof TableCreateButton;

  /**
   * TableFormiterator allows a child EditButton which will render an edit button in the ActionsMenu of each row.
   * The EditButton will open a dialog with the form fields passed as children.
   *
   * @example
   *  <Grid container>
   *    <Grid item xs={12}>
   *       <ArrayInput label={false} source="descriptor.fields">
   *           <TableFormIterator label="Property catalog">
   *
   *               <TableFormIterator.EditButton>
   *                  <TextInput label="Name" source={'name'} validate={required()} />
   *                  <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *               </TableFormIterator.EditButton>
   *
   *               <TextInput label="Name" source={'name'} validate={required()} />
   *               <TextInput label="Denomination" source={'denomination'} helperText={false} />
   *               <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *            </TableFormIterator>
   *       </ArrayInput>
   *     </Grid>
   *   </Grid>
   */
  EditButton: typeof EditButton;
  /**
   * TableFormiterator allows a child Field which will render a read-only field in the Table with a custom record context.
   * The record context will overwritthe by getValues() of useFormContext() hook.
   *
   * @example
   *  <Grid container>
   *    <Grid item xs={12}>
   *       <ArrayInput label={false} source="descriptor.fields">
   *           <TableFormIterator label="Property catalog">
   *
   *               <TableFormIterator.EditButton>
   *                  <TextInput label="Name" source={'name'} validate={required()} />
   *                  <SelectInput choices={FieldTypes} source={'type'} helperText={false} />
   *               </TableFormIterator.EditButton>
   *
   *               <TableFormIterator.Field source="name">
   *                  <TextField />
   *               </TableFormIterator.Field>
   *               <TableFormIterator.Field source="type">
   *                  <TextField />
   *               </TableFormIterator.Field>
   *               {...}
   *            </TableFormIterator>
   *       </ArrayInput>
   *     </Grid>
   *   </Grid>
   */
  Field: typeof Field;
};

const DefaultTableForm = TableFormIterator as ITableForm;

DefaultTableForm.CreateButton = TableCreateButton;
DefaultTableForm.EditButton = EditButton;
DefaultTableForm.Field = Field;

export { DefaultTableForm as TableFormIterator };
export type { TableFormIteratorProps };
