// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import type { MouseEvent } from 'react';
import React, { useCallback } from 'react';
import { animated } from '@react-spring/web';
import { Button, ButtonVariant } from './Button';
import type { LocalizerType } from '../types/Util';
import { ModalHost } from './ModalHost';
import { ModalPage } from './Modal';
import type { Theme } from '../util/theme';
import { useAnimated } from '../hooks/useAnimated';
import { Spinner } from './Spinner';

export type ActionSpec = {
  action: () => unknown;
  style?: 'affirmative' | 'negative';
  autoClose?: boolean;
  disabled?: boolean;
  'aria-disabled'?: boolean;
} & (
  | {
      text: string;
      id?: string;
    }
  | {
      text: string | JSX.Element;
      id: string;
    }
);

export type OwnProps = Readonly<{
  actions?: Array<ActionSpec>;
  dialogName: string;
  cancelButtonVariant?: ButtonVariant;
  cancelText?: string;
  isSpinning?: boolean;
  children?: React.ReactNode;
  hasXButton?: boolean;
  i18n: LocalizerType;
  moduleClassName?: string;
  noMouseClose?: boolean;
  noDefaultCancelButton?: boolean;
  onCancel?: () => unknown;
  onClose: () => unknown;
  onTopOfEverything?: boolean;
  theme?: Theme;
  title?: React.ReactNode;
}>;

export type Props = OwnProps;

function focusRef(el: HTMLElement | null) {
  if (el) {
    el.focus();
  }
}

function getButtonVariant(
  buttonStyle?: 'affirmative' | 'negative'
): ButtonVariant {
  if (buttonStyle === 'affirmative') {
    return ButtonVariant.Primary;
  }

  if (buttonStyle === 'negative') {
    return ButtonVariant.Destructive;
  }

  return ButtonVariant.Secondary;
}

export const ConfirmationDialog = React.memo(function ConfirmationDialogInner({
  actions = [],
  dialogName,
  cancelButtonVariant,
  cancelText,
  children,
  hasXButton,
  i18n,
  isSpinning,
  moduleClassName,
  noMouseClose,
  noDefaultCancelButton,
  onCancel,
  onClose,
  onTopOfEverything,
  theme,
  title,
}: Props) {
  const { close, overlayStyles, modalStyles } = useAnimated(onClose, {
    getFrom: () => ({ opacity: 0, transform: 'scale(0.25)' }),
    getTo: isOpen => ({ opacity: isOpen ? 1 : 0, transform: 'scale(1)' }),
  });

  const cancelAndClose = useCallback(() => {
    if (onCancel) {
      onCancel();
    }
    close();
  }, [close, onCancel]);

  const handleCancel = useCallback(
    (e: MouseEvent) => {
      if (e.target === e.currentTarget) {
        cancelAndClose();
      }
    },
    [cancelAndClose]
  );

  const hasActions = Boolean(actions.length);

  const footer = (
    <>
      {!isSpinning && !noDefaultCancelButton ? (
        <Button
          onClick={handleCancel}
          ref={focusRef}
          variant={
            cancelButtonVariant ||
            (hasActions ? ButtonVariant.Secondary : ButtonVariant.Primary)
          }
        >
          {cancelText || i18n('icu:confirmation-dialog--Cancel')}
        </Button>
      ) : null}
      {actions.map((action, i) => (
        <Button
          key={
            typeof action.text === 'string'
              ? (action.id ?? action.text)
              : action.id
          }
          disabled={action.disabled || isSpinning}
          aria-disabled={action['aria-disabled']}
          onClick={() => {
            action.action();
            if (action.autoClose !== false) {
              close();
            }
          }}
          data-action={i}
          variant={getButtonVariant(action.style)}
        >
          {isSpinning ? (
            <Spinner
              size="22px"
              svgSize="small"
              direction="on-primary-button"
            />
          ) : (
            action.text
          )}
        </Button>
      ))}
    </>
  );

  const modalName = `ConfirmationDialog.${dialogName}`;

  return (
    <ModalHost
      modalName={modalName}
      noMouseClose={noMouseClose}
      onClose={close}
      onEscape={cancelAndClose}
      onTopOfEverything={onTopOfEverything}
      overlayStyles={overlayStyles}
      theme={theme}
      moduleClassName={
        moduleClassName ? `${moduleClassName}__ModalHost` : undefined
      }
    >
      <animated.div style={modalStyles}>
        <ModalPage
          modalName={modalName}
          hasXButton={hasXButton}
          i18n={i18n}
          moduleClassName={moduleClassName}
          onClose={cancelAndClose}
          title={title}
          modalFooter={footer}
        >
          {children}
        </ModalPage>
      </animated.div>
    </ModalHost>
  );
});