import { Button as RaButton } from './Button';
import { Toolbar } from '@/components/ra-forms';
import { PlusCircleOutlined } from '@ant-design/icons';
import { Breakpoint, Button, Dialog, DialogContent } from '@mui/material';
import { CreateBase, UseCreateMutateParams, useNotify, useResourceContext, useTranslate } from 'ra-core';
import { ButtonProps, CreateButton, CreateButtonProps, CreateProps, SaveButton } from 'ra-ui-materialui';
import React, { Children, PropsWithChildren, useCallback, useState } from 'react';
import { FieldValues, useFormContext } from 'react-hook-form';
import { useQueryClient } from 'react-query';

function updateColl(old: any, data: any) {
  const id = data.id;
  if (!old) return [data];
  const index = old.findIndex((record: any) => record.id === id);
  if (index === -1) {
    return [...old, data];
  }

  return [...old.slice(0, index), { ...old[index], ...data }, ...old.slice(index + 1)];
}
function setManyReferenceQueryData(res: any, data: any) {
  const result = res && res.data ? { data: updateColl(res.data, data), total: res.total + 1 } : res;
  return result;
}
function setManyQueryData(coll: any, data: any) {
  return coll && coll.length > 0 ? updateColl(coll, data) : coll;
}
function setListQueryData(res: any, data: any) {
  return res && res.data ? { ...res, data: updateColl(res.data, data), total: res.total + 1 } : res;
}

type ResponsiveButtonProps = CreateButtonProps &
  ButtonProps & {
    label?: string;
    onClick?: () => void;
    disableFloatingButton?: boolean;
  };
function ResponsiveButton(props: ResponsiveButtonProps): JSX.Element {
  const { disableFloatingButton, label = 'ra.action.create', startIcon = <PlusCircleOutlined />, ...rest } = props;
  const handleClick = useCallback(
    (event: any) => {
      (event as Event).preventDefault();
      props?.onClick?.();
    },
    [props]
  );

  return disableFloatingButton ? (
    <RaButton {...rest} onClick={handleClick} label={label}>
      {startIcon as any}
    </RaButton>
  ) : (
    <CreateButton {...props} onClick={handleClick} label={label}>
      {startIcon as any}
    </CreateButton>
  );
}
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 translate = useTranslate();
  const handleClick = useCallback(
    (event: any) => {
      (event as Event).preventDefault();
      (event as Event).stopPropagation();
      handleSubmit((fieldValues) => onSubmit(fieldValues, closeDialog))();
    },
    [handleSubmit, onSubmit, closeDialog]
  );
  return (
    <Button onClick={handleClick} color="primary" variant="contained" size="medium">
      {translate('ra.action.save')}
    </Button>
  );
}
type CreateInDialogButtonProps = PropsWithChildren &
  CreateProps &
  ResponsiveButtonProps & {
    maxWidth: Breakpoint;
    fullWidth?: boolean;
    fullScreen?: boolean;
    /**
     * Indicates whether the floating button should be disabled.
     * Default is false, this mean that the floating button will be displayed on small screens.
     * **If you have multiple buttons on the same page, they will be displayed over each other! **
     */
    disableFloatingButton?: 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();
     * }
     * <CreateInDialogButton onSubmit={handleSubmit}>
     *  <SimpleForm>
     *    <TextInput source="title" />
     *  </SimpleForm>
     * </CreateInDialogButton>
     *
     */
    onSubmit?: SubmitFunction;
    /**
     * Controlled open state. When provided, the dialog becomes externally controlled
     * (the internal state is ignored). Enables opening the dialog programmatically.
     * 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, where only the dialog surface is needed. Defaults to false.
     */
    hideTrigger?: boolean;
  };
function CreateInDialogButton(props: CreateInDialogButtonProps) {
  const {
    children,
    maxWidth = 'md',
    fullWidth = true,
    fullScreen,
    hideTrigger = false,
    open: openProp,
    onOpenChange,
    onSubmit,
    mutationOptions,
    scroll = 'body',
    ...rest
  } = props;
  const queryClient = useQueryClient();
  const resource = useResourceContext();
  const translate = useTranslate();
  const Child = Children.only(children);
  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 notify = useNotify();
  const handleOpen = useCallback(() => setOpenState(true), [setOpenState]);
  const handleClose = useCallback(() => setOpenState(false), [setOpenState]);
  const handleSuccess = useCallback(
    (data: any, params: UseCreateMutateParams<any>, context: unknown) => {
      const now = Date.now();
      const updatedAt = now;

      queryClient.setQueryData([resource, 'getOne', { id: data.id }], data);
      queryClient.setQueriesData([resource, 'getList'], (res: any) => setListQueryData(res, data), {
        updatedAt
      });
      queryClient.setQueriesData([resource, 'getMany'], (coll: any) => setManyQueryData(coll, data), {
        updatedAt
      });
      queryClient.setQueriesData([resource, 'getManyReference'], (res: any) => setManyReferenceQueryData(res, data), {
        updatedAt
      });
      handleClose();
      notify('ra.notification.created', { messageArgs: { smart_count: 1 } });
      props?.mutationOptions?.onSuccess?.(data, params, context);
    },
    [handleClose, notify, props.mutationOptions, queryClient, resource]
  );

  return (
    <>
      {!hideTrigger && <ResponsiveButton {...(rest as any)} onClick={handleOpen} />}

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

export { CreateInDialogButton };
export type { CreateInDialogButtonProps };
