import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import {
  ConfirmDialog,
  ConfirmationConfig,
  useConfirmationDelay,
} from '../../ConfirmDialog';
import { IconName, Icons } from '../../Icons';
import {
  ActionData,
  ContextActionData,
  NavigationActionData,
} from '../Actions.models';
import classes from './Action.scss';

export interface ActionProps {
  /** Action to perform */
  action: ActionData;
  onActionClick?: ActionClickHandler;
}

export type ActionClickHandler = (
  e: React.MouseEvent<HTMLDivElement, MouseEvent>,
  action: ContextActionData,
) => void;

/**
 * Simple Confirm message
 * @example
 * <SimpleConfirmDialog />
 */
const SimpleConfirmDialog: React.FC<{
  onConfirmOpen: ConfirmationConfig['onConfirmOpen'];
}> = ({ onConfirmOpen }) => {
  useEffect(() => {
    onConfirmOpen && onConfirmOpen(true);
    return () => {
      onConfirmOpen && onConfirmOpen(false);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return <span data-test-id="confirm">Click again to confirm</span>;
};

/**
 * Renders an action a user can click on
 * @example
 * // Action with click handler
 * <Action
 *  action: {{
 *    label: 'Context Action',
 *    onActionSelected={clickHandler}
 *  }}
 * />
 * // An anchor tag leading to the path within the application
 * <Action
 *  action: {{
 *    label: 'Navigation Action',
 *    path={path}
 *  }}
 * />
 */
export const Action: React.FC<ActionProps> = ({ action, onActionClick }) =>
  isNavigationAction(action) ? (
    <NavigationAction action={action} />
  ) : (
    <ContextAction action={action} onActionClick={onActionClick} />
  );

/**
 * Renders HTML element with onClick handler and specified confirmation mode
 * @example
 * <ContextAction action={action} onActionClick={onActionClick}/>
 */
const ContextAction: React.FC<{
  action: ContextActionData;
  onActionClick?: ActionClickHandler;
}> = ({ action, onActionClick }) => {
  const {
    label,
    icon,
    confirmationMode = 'None',
    confirmationConfig = {},
    onActionSelected,
    isDisabled = false,
  } = action;
  const [confirmation, setConfirmation] = useState<boolean>(false);
  const [referenceElement, setReferenceElement] = useState<HTMLElement>();
  const { isMouseOver, setIsMouseOver } = useConfirmationDelay({
    confirmationMode,
    confirmation,
    setConfirmation,
  });

  const { title, body, cancelButtonText, confirmButtonText, onConfirmOpen } =
    confirmationConfig;

  // Action has been clicked
  const onClickHandler: React.MouseEventHandler<HTMLDivElement> = (e): void => {
    switch (confirmationMode) {
      case 'Simple':
        !confirmation
          ? setConfirmation(true)
          : (onActionSelected?.(), setConfirmation(false));
        break;
      case 'Advanced':
        setConfirmation(true);
        break;
      default:
        onActionSelected?.();
        setConfirmation(false);
        break;
    }
    onActionClick?.(e, action);
  };

  return (
    <div
      className={clsx(
        classes.container,
        classes.context,
        {
          [classes.hasConfirm]: confirmation,
          [classes.hasIcon]: icon !== undefined,
          [classes.isDisabled]: isDisabled,
        },
        'action-container',
      )} // Show different bg-color when confirmation is displayed
      ref={setReferenceElement as React.LegacyRef<HTMLDivElement>}
      onClick={isDisabled ? undefined : onClickHandler}
      onMouseEnter={() => setIsMouseOver(true)}
      onMouseLeave={() => setIsMouseOver(false)}
      onMouseOver={() => {
        // Needed in cases where the mouse enters the component after another confirmation dialog was dismissed
        if (!isMouseOver) {
          setIsMouseOver(true);
        }
      }}
      data-test-id="action"
    >
      {confirmation && confirmationMode === 'Simple' ? (
        <SimpleConfirmDialog onConfirmOpen={onConfirmOpen} />
      ) : (
        <span data-test-id="label">{label}</span>
      )}
      {icon !== undefined && !confirmation && (
        <Icons icon={icon} className={classes.icon} />
      )}
      {confirmation && confirmationMode === 'Advanced' && (
        <ConfirmDialog
          referenceElement={referenceElement}
          title={title}
          cancelButtonText={cancelButtonText}
          confirmButtonText={confirmButtonText}
          offsetSkidding={1}
          onCancel={() => setConfirmation(false)}
          onConfirm={() => {
            onActionSelected?.();
            setConfirmation(false);
          }}
          onConfirmOpen={
            confirmationMode === 'Advanced' && onConfirmOpen !== undefined
              ? onConfirmOpen
              : undefined
          }
        >
          {body}
        </ConfirmDialog>
      )}
    </div>
  );
};

/**
 * Renders HTML anchor element
 * @example
 * <NavigationAction action={{ path: '#', label: 'Navigation Action'}} />
 */
const NavigationAction: React.FC<{ action: NavigationActionData }> = ({
  action,
}) => {
  const {
    path,
    openInNewTab = false,
    label,
    icon,
    isDisabled = false,
  } = action;
  const defaultIcon = openInNewTab ? IconName.External : IconName.ChevronRight;
  return (
    <Link
      to={path}
      className={clsx(
        classes.container,
        classes.containerLink,
        classes.hasIcon,
        {
          [classes.isDisabled]: isDisabled,
        },
        'action-container',
      )}
      data-test-id="action"
      target={openInNewTab ? '_blank' : undefined}
    >
      <span data-test-id="label">{label}</span>
      <Icons icon={icon ? icon : defaultIcon} className={classes.icon} />
    </Link>
  );
};

/**
 * Determines whether an action data object is a NavigationActionData object.
 * @param {ActionData} action - The action data object to check.
 * @returns {boolean} - Whether the action data object is a NavigationActionData object.
 */
export function isNavigationAction(
  action: ActionData<unknown>,
): action is NavigationActionData {
  return 'path' in action && !!action.path;
}

/**
 * Determines whether an action data object is a ContextActionData object.
 * @param {ActionData} action - The action data object to check.
 * @returns {boolean} - Whether the action data object is a ContextActionData object.
 */
export function isContextAction(
  action: ActionData<unknown>,
): action is ContextActionData {
  return 'onActionSelected' in action && !!action.onActionSelected;
}
