import type { AlertStatus } from '@chakra-ui/alert';
import { Alert, AlertDescription, AlertIcon, AlertTitle } from '@chakra-ui/alert';
import { CloseButton } from '@chakra-ui/close-button';
import { AlertIconProps, toast } from '@chakra-ui/react';
import { chakra, ColorMode, ColorModeContext, ThemeProvider, useChakra } from '@chakra-ui/system';
import defaultTheme from '@chakra-ui/theme';
import { isFunction, noop } from '@chakra-ui/utils';
import * as React from 'react';
import { RenderProps, ToastId, ToastOptions } from './toast.types';

export interface UseToastOptions {
  /**
   * The placement of the toast
   *
   * @default bottom
   */
  position?: ToastOptions['position'];
  /**
   * The delay before the toast hides (in milliseconds) If set to `null`, toast will never dismiss.
   *
   * @default 5000 ( = 5000ms )
   */
  duration?: ToastOptions['duration'];
  /** Render a component toast component. Any component passed will receive 2 props: `id` and `onClose`. */
  render?(props: RenderProps): React.ReactNode;
  /** The title of the toast */
  title?: React.ReactNode;
  /** The description of the toast */
  description?: React.ReactNode;
  /** If `true`, toast will show a close button */
  isClosable?: boolean;
  /** The alert component `variant` to use */
  variant?: string;
  /** The status of the toast. */
  status?: AlertStatus;
  /**
   * The `id` of the toast.
   *
   * Mostly used when you need to prevent duplicate. By default, we generate a unique `id` for each toast
   */
  id?: ToastId;
  /** Callback function to run side effects after the toast has closed. */
  onCloseComplete?: () => void;
  /** 图标属性 */
  iconProps?: AlertIconProps;
}

export type IToast = UseToastOptions;

const Toast: React.FC<any> = props => {
  const {
    // 默认为成功黑色
    status = 'success',
    variant,
    id,
    title,
    isClosable = true,
    onClose,
    description,
    iconProps,
  } = props;

  return (
    <Alert
      status={status}
      // hack: 为成功时，自定义背景色
      {...(status === 'success' && { colorScheme: 'toast' })}
      variant={variant}
      id={id}
      alignItems="start"
      borderRadius="md"
      boxShadow="lg"
      paddingEnd={8}
      textAlign="start"
      width="auto"
    >
      <AlertIcon {...iconProps} />
      <chakra.div flex="1" maxWidth="100%">
        {title && <AlertTitle>{title}</AlertTitle>}
        {description && <AlertDescription display="block">{description}</AlertDescription>}
      </chakra.div>
      {isClosable && (
        <CloseButton size="sm" onClick={onClose} position="absolute" insetEnd={1} top={1} />
      )}
    </Alert>
  );
};

const defaults = {
  duration: 5000,
  position: 'bottom',
  variant: 'solid',
} as const;

export type CreateStandAloneToastParam = Partial<
  {
    setColorMode: (value: ColorMode) => void;
  } & ReturnType<typeof useChakra> & { defaultOptions: UseToastOptions }
>;

export const defaultStandaloneParam: Required<CreateStandAloneToastParam> = {
  theme: defaultTheme,
  colorMode: 'light',
  toggleColorMode: noop,
  setColorMode: noop,
  defaultOptions: defaults,
};
/** Create a toast from outside of React Components */
export function createStandaloneToast({
  theme = defaultStandaloneParam.theme,
  colorMode = defaultStandaloneParam.colorMode,
  toggleColorMode = defaultStandaloneParam.toggleColorMode,
  setColorMode = defaultStandaloneParam.setColorMode,
  defaultOptions = defaultStandaloneParam.defaultOptions,
}: CreateStandAloneToastParam = defaultStandaloneParam) {
  const renderWithProviders = (
    props: React.PropsWithChildren<RenderProps>,
    options: UseToastOptions,
  ) => (
    <ThemeProvider theme={theme}>
      <ColorModeContext.Provider value={{ colorMode, setColorMode, toggleColorMode }}>
        {isFunction(options.render) ? options.render(props) : <Toast {...props} {...options} />}
      </ColorModeContext.Provider>
    </ThemeProvider>
  );

  const toastImpl = (options?: UseToastOptions) => {
    const opts = { ...defaultOptions, ...options };

    const Message: React.FC<RenderProps> = props => renderWithProviders(props, opts);

    return toast.notify(Message, opts);
  };

  toastImpl.close = toast.close;
  toastImpl.closeAll = toast.closeAll;

  // toasts can only be updated if they have a valid id
  toastImpl.update = (id: ToastId, options: Omit<UseToastOptions, 'id'>) => {
    if (!id) return;

    const opts = { ...defaultOptions, ...options };

    toast.update(id, {
      ...opts,
      message: props => renderWithProviders(props, opts),
    });
  };

  toastImpl.isActive = toast.isActive;

  return toastImpl;
}

/** React hook used to create a function that can be used to show toasts in an application. */
export function useToast(options?: UseToastOptions) {
  const { theme, setColorMode, toggleColorMode, colorMode } = useChakra();
  return React.useMemo(
    () =>
      createStandaloneToast({
        theme,
        colorMode,
        setColorMode,
        toggleColorMode,
        defaultOptions: options,
      }),
    [theme, setColorMode, toggleColorMode, colorMode, options],
  );
}

export default useToast;
