import { clsx } from 'clsx';
import { useState, useRef, useEffect } from 'react';

import Body from '../body/Body';
import {
  CloseButton,
  Sentiment,
  Size,
  Status,
  Typography,
  WDS_LIVE_REGION_DELAY_MS,
} from '../common';

import StatusIcon from '../statusIcon';
import Title from '../title/Title';
import { logActionRequired } from '../utilities';

import InlineMarkdown from './inlineMarkdown';
import Button from '../button';
import Link from '../link';

/**
 * @deprecated `Alert` component is being replaced by the `InfoPrompt` component
 */
export type AlertAction = {
  'aria-label'?: string;
  href?: string;
  target?: string;
  text: React.ReactNode;
  onClick?: () => void;
  /** Controls the rendered element: `'link'` (default) renders a Link, `'button'` renders a Button. When `'button'` is used with `href`, renders an anchor styled as a button. */
  as?: 'button' | 'link';
};

/** @deprecated Use `"top" | "bottom"` instead. */
type AlertTypeDeprecated = `${Sentiment.SUCCESS | Sentiment.INFO | Sentiment.ERROR}`;
type AlertTypeResolved = `${
  | Sentiment.POSITIVE
  | Sentiment.NEUTRAL
  | Sentiment.WARNING
  | Sentiment.NEGATIVE
  // remove when all instances of Sentiment.PENDING have been updated to Status.PENDING
  | Sentiment.PENDING
  | Status.PENDING}`;

/**
 * @deprecated `Alert` component is being replaced by the `InfoPrompt` component
 */
export type AlertType = AlertTypeResolved | AlertTypeDeprecated;

/**
 * @deprecated `Alert` component is being replaced by the `InfoPrompt` component
 */
export enum AlertArrowPosition {
  TOP_LEFT = 'up-left',
  TOP = 'up-center',
  TOP_RIGHT = 'up-right',
  BOTTOM_LEFT = 'down-left',
  BOTTOM = 'down-center',
  BOTTOM_RIGHT = 'down-right',
}

/**
 * @deprecated `Alert` component is being replaced by the `InfoPrompt` component
 */
export interface AlertProps {
  /** An optional call to action to sit under the main body of the alert. If your label is short, use aria-label to provide more context */
  action?: AlertAction;
  className?: string;
  /** An optional icon. If not provided, we will default the icon to something appropriate for the type */
  icon?: React.ReactNode;
  /**
   * Override for [StatusIcon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
   * announced by the screen readers
   * */
  statusIconLabel?: string;
  /** Title for the alert component */
  title?: string;
  /** The main body of the alert. Accepts plain text and bold words specified with **double stars */
  message?: string;
  /** The presence of the onDismiss handler will trigger the visibility of the close button */
  onDismiss?: React.MouseEventHandler<HTMLButtonElement>;
  /**
   * The type dictates which icon and colour will be used
   * @default 'neutral'
   */
  type?: AlertType;
  /** @deprecated Use `InlinePrompt` instead. */
  arrow?: `${AlertArrowPosition}`;
  /** @deprecated Use `message` instead. Be aware `message` only accepts plain text or text with **bold** markdown. */
  children?: React.ReactNode;
  /** @deprecated Use `onDismiss` instead. */
  dismissible?: boolean;
  /** @deprecated Alert component doesn't support `size` anymore, please remove this prop. */
  size?: `${Size}`;
}

function resolveType(type: AlertType): AlertTypeResolved {
  switch (type) {
    case 'success':
      return 'positive';
    case 'info':
      return 'neutral';
    case 'error':
      return 'negative';
    default:
      return type;
  }
}

/**
 * @deprecated Use [`InfoPrompt`](https://storybook.wise.design/?path=/docs/prompts-infoprompt--docs) component instead. Run codemod to migrate: **`npx @wise/wds-codemods@latest info-prompt`**
 */
export default function Alert({
  action,
  className,
  icon,
  statusIconLabel,
  onDismiss,
  message,
  title,
  type = 'neutral',
  arrow,
  children,
  size,
  dismissible,
}: AlertProps) {
  useEffect(() => {
    if (arrow !== undefined) {
      logActionRequired(
        "Alert component doesn't support 'arrow' anymore, use 'InlinePrompt' instead.",
      );
    }
  }, [arrow]);

  useEffect(() => {
    if (children !== undefined) {
      logActionRequired(
        "Alert component doesn't support 'children' anymore, use 'message' instead.",
      );
    }
  }, [children]);

  useEffect(() => {
    if (dismissible !== undefined) {
      logActionRequired(
        "Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.",
      );
    }
  }, [dismissible]);

  useEffect(() => {
    if (size !== undefined) {
      logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
    }
  }, [size]);

  const resolvedType = resolveType(type);
  useEffect(() => {
    if (resolvedType !== type) {
      logActionRequired(
        `Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`,
      );
    }
  }, [resolvedType, type]);

  const [shouldFire, setShouldFire] = useState<boolean>();

  const [shouldAnnounce, setShouldAnnounce] = useState<boolean>(false);
  useEffect(() => {
    const timeoutId = setTimeout(() => {
      setShouldAnnounce(true);
    }, WDS_LIVE_REGION_DELAY_MS);

    return () => clearTimeout(timeoutId);
  }, []);

  const closeButtonReference = useRef<HTMLButtonElement>(null);

  /**
   * All focusable elements must be disabled until we announce the alert.
   * @see https://dequeuniversity.com/rules/axe/4.10/aria-hidden-focus?application=axeAPI
   */
  const accessibleTabIndex = shouldAnnounce ? undefined : -1;

  return (
    <div
      role={resolvedType === Sentiment.NEGATIVE ? 'alert' : 'status'}
      className="wds-alert__liveRegion"
    >
      <div
        aria-hidden={shouldAnnounce ? undefined : 'true'}
        className={clsx(
          'alert d-flex',
          `alert-${resolvedType}`,
          arrow != null && alertArrowClassNames(arrow),
          className,
        )}
        data-testid="alert"
        onTouchStart={() => setShouldFire(true)}
        onTouchEnd={(event) => {
          if (
            shouldFire &&
            action?.href &&
            // Check if current event is triggered from closeButton
            event.target instanceof Node &&
            closeButtonReference.current &&
            !closeButtonReference.current.contains(event.target)
          ) {
            if (action.target === '_blank') {
              window.top?.open(action.href);
            } else {
              window.top?.location.assign(action.href);
            }
          }
          setShouldFire(false);
        }}
        onTouchMove={() => setShouldFire(false)}
      >
        <div className="alert__icon">
          {icon || <StatusIcon size={32} sentiment={resolvedType} iconLabel={statusIconLabel} />}
        </div>
        <div className={clsx('alert__content', 'd-flex', 'flex-grow-1')}>
          <div className="alert__message">
            <div>
              {title && (
                <Title className="m-b-1" type={Typography.TITLE_BODY}>
                  {title}
                </Title>
              )}
              <Body as="span" className="d-block" type={Typography.BODY_LARGE}>
                {children || <InlineMarkdown>{message}</InlineMarkdown>}
              </Body>
            </div>
            {action &&
              ('href' in action && action.as !== 'button' ? (
                <Link
                  href={action.href}
                  aria-label={action['aria-label']}
                  target={action.target}
                  type={Typography.LINK_LARGE}
                  className="alert__action"
                  tabIndex={accessibleTabIndex}
                  onClick={action.onClick}
                >
                  {action.text}
                </Link>
              ) : (
                <Button
                  v2
                  as={action.href ? 'a' : 'button'}
                  href={action.href}
                  target={action.target}
                  size="sm"
                  sentiment="default"
                  aria-label={action['aria-label']}
                  priority="secondary-neutral"
                  className="alert__action"
                  tabIndex={accessibleTabIndex}
                  onClick={action.onClick}
                >
                  {action.text}
                </Button>
              ))}
          </div>
        </div>
        {onDismiss && (
          <div className="alert__close">
            <CloseButton
              ref={closeButtonReference}
              size="xs"
              tabIndex={accessibleTabIndex}
              onClick={onDismiss}
            />
          </div>
        )}
      </div>
    </div>
  );
}

function alertArrowClassNames(arrow: `${AlertArrowPosition}`) {
  switch (arrow) {
    case 'down-center':
      return 'arrow arrow-bottom arrow-center';
    case 'down-left':
      return 'arrow arrow-bottom arrow-left';
    case 'down-right':
      return 'arrow arrow-bottom arrow-right';
    case 'up-center':
      return 'arrow arrow-center';
    case 'up-right':
      return 'arrow arrow-right';
    case 'up-left':
    default:
      return 'arrow';
  }
}
