import { Button } from './Button';
import { Toolbar } from '@/components/ra-forms';
import { Breakpoint, Dialog, DialogContent } from '@mui/material';
import { EditBase, UseUpdateMutateParams, useNotify, useRecordContext } from 'ra-core';
import { EditButton, EditProps, SaveButton } from 'ra-ui-materialui';
import React, { Children, PropsWithChildren, useCallback, useState } from 'react';
import { FieldValues, useFormContext } from 'react-hook-form';

type ResponsiveButtonProps = {
  label?: string;
  onClick: () => void;
};

function ResponsiveButton(props: ResponsiveButtonProps): JSX.Element {
  const handleClick = useCallback(
    (event: any) => {
      (event as Event).preventDefault();
      (event as Event).stopPropagation();
      props?.onClick?.();
    },
    [props]
  );
  // @ts-ignore - I need to allow the label prop to be a ReactNode
  return <EditButton {...props} onClick={handleClick} />;
}

type CloseDialogFunction = () => void;
type SubmitFunction = (values: FieldValues, closeDialog: CloseDialogFunction) => void;
type SubmitButtonProps = {
  onSubmit: SubmitFunction;
  closeDialog: CloseDialogFunction;
};
function SubmitButton(props: SubmitButtonProps) {
  const { onSubmit, closeDialog } = props;
  const { handleSubmit } = useFormContext();
  const handleClick = useCallback(
    (event: any) => {
      (event as Event).preventDefault();
      (event as Event).stopPropagation();
      handleSubmit((fieldValues) => onSubmit(fieldValues, closeDialog))();
    },
    [handleSubmit, onSubmit, closeDialog]
  );
  return (
    <Button
      disableIconOnly
      onClick={handleClick}
      color="primary"
      variant="contained"
      size="medium"
      label="ra.action.save"
    />
  );
}
type EditInDialogButtonProps = PropsWithChildren &
  EditProps &
  ResponsiveButtonProps & {
    maxWidth: Breakpoint;
    fullWidth?: boolean;
    fullScreen?: boolean;
    scroll: 'paper' | 'body' | undefined;
    /**
     * Custom handler for the submit event, in this case you are responsible for closing the dialog.
     * @example
     *
     * const handleSubmit = (values, closeDialog) => {
     *  console.log(values); // { title: 'Hello World' }
     *  closeDialog();
     * }
     * <EditInDialogButton onSubmit={handleSubmit}>
     *  <SimpleForm>
     *    <TextInput source="title" />
     *  </SimpleForm>
     * </EditInDialogButton>
     *
     */
    onSubmit?: SubmitFunction;
    /**
     * Controlled open state. When provided, the dialog becomes externally controlled
     * (the internal state is ignored). Enables opening the dialog programmatically,
     * e.g. from a `Datagrid` `rowClick` handler. Omit to keep the default uncontrolled behavior.
     */
    open?: boolean;
    /**
     * Called when the dialog should open or close. Use together with `open`.
     * Receives the next open state.
     */
    onOpenChange?: (open: boolean) => void;
    /**
     * When true, the trigger button is not rendered. Useful when the dialog is driven
     * externally (e.g. by `rowClick`), where only the dialog surface is needed.
     * Defaults to false (renders the trigger button).
     */
    hideTrigger?: boolean;
  };
function EditInDialogButton(props: EditInDialogButtonProps): JSX.Element {
  const {
    children,
    maxWidth = 'md',
    fullWidth = true,
    fullScreen,
    hideTrigger = false,
    open: openProp,
    onOpenChange,
    mutationOptions,
    onSubmit,
    scroll = 'body',
    ...rest
  } = props;
  const notify = useNotify();
  const isControlled = openProp !== undefined;
  const [internalOpen, setInternalOpen] = useState(false);
  const open = isControlled ? openProp : internalOpen;
  const setOpenState = useCallback(
    (next: boolean) => {
      if (!isControlled) setInternalOpen(next);
      onOpenChange?.(next);
    },
    [isControlled, onOpenChange]
  );
  const Child = Children.only(children);
  const handleOpen = useCallback(() => setOpenState(true), [setOpenState]);
  const handleClose = useCallback(() => setOpenState(false), [setOpenState]);
  const handleSuccess = useCallback(
    (data: any, params: UseUpdateMutateParams<any>, context: unknown) => {
      handleClose();
      notify('ra.notification.updated', {
        messageArgs: { smart_count: 1 },
        undoable: props?.mutationMode === 'undoable'
      });
      props?.mutationOptions?.onSuccess?.(data, params, context);
    },
    [handleClose, notify, props.mutationOptions, props.mutationMode]
  );
  const record = useRecordContext();
  return (
    <>
      {!hideTrigger && <ResponsiveButton {...rest} onClick={handleOpen} />}

      {record ? (
        <Dialog
          open={open}
          scroll={scroll}
          onClose={handleClose}
          fullWidth={fullWidth}
          maxWidth={maxWidth}
          fullScreen={fullScreen}
          keepMounted={false}
        >
          <DialogContent sx={{ m: 0, p: 0 }}>
            <EditBase
              {...props}
              id={record.id}
              mutationMode="pessimistic"
              mutationOptions={{
                ...mutationOptions,
                onSuccess: handleSuccess
              }}
            >
              {React.isValidElement(Child)
                ? React.cloneElement(Child, {
                    ...Child.props,
                    modal: true,
                    toolbar: (
                      <Toolbar>
                        <Button
                          disableIconOnly
                          variant="text"
                          size="medium"
                          onClick={handleClose}
                          label="ra.action.cancel"
                        />
                        {onSubmit ? (
                          <SubmitButton onSubmit={onSubmit!} closeDialog={handleClose} />
                        ) : (
                          <SaveButton type="button" />
                        )}
                      </Toolbar>
                    )
                  })
                : null}
            </EditBase>
          </DialogContent>
        </Dialog>
      ) : null}
    </>
  );
}

export { EditInDialogButton };
export type { EditInDialogButtonProps };
