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

import Body from '../body/Body';
import { Sentiment, Size, Typography, Variant } from '../common';
import { CloseButton } from '../common/closeButton';
import StatusIcon from '../statusIcon';
import Title from '../title/Title';
import { logActionRequired } from '../utilities';

import InlineMarkdown from './inlineMarkdown';
import { Action } from '../common/action/Action';

export type AlertAction = {
  'aria-label'?: string;
  href?: string;
  target?: string;
  text: React.ReactNode;
  onClick?: () => void;
};

/** @deprecated Use `"top" | "bottom"` instead. */
type AlertTypeDeprecated = `${Sentiment.SUCCESS | Sentiment.INFO | Sentiment.ERROR}`;
type AlertTypeResolved = `${
  | Sentiment.POSITIVE
  | Sentiment.NEUTRAL
  | Sentiment.WARNING
  | Sentiment.NEGATIVE}`;
export type AlertType = AlertTypeResolved | AlertTypeDeprecated;

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',
}

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;
  /** 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 */
  type?: AlertType;
  variant?: `${Variant}`;
  /** Controls rendering of the Alert component. <br /> Toggle this prop instead using conditional rendering and logical AND (&&) operator, to make the component work with screen readers. */
  active?: boolean;
  /** @deprecated Use `InlineAlert` 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;
  }
}

export default function Alert({
  arrow,
  action,
  children,
  className,
  dismissible,
  icon,
  onDismiss,
  message,
  size,
  title,
  type = 'neutral',
  variant = 'desktop',
  active = true,
}: AlertProps) {
  useEffect(() => {
    if (arrow !== undefined) {
      logActionRequired(
        "Alert component doesn't support 'arrow' anymore, use 'InlineAlert' 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(false);

  const closeButtonReference = useRef<HTMLButtonElement>(null);

  return (
    <div role={resolvedType === Sentiment.NEGATIVE ? 'alert' : 'status'}>
      {active && (
        <div
          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={clsx('alert__content', 'd-flex', 'flex-grow-1', variant)}
            data-testid={variant}
          >
            {icon ? (
              <div className="alert__icon">{icon}</div>
            ) : (
              <StatusIcon size={Size.LARGE} sentiment={resolvedType} />
            )}
            <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 && <Action action={action} variant="action-button" className="m-t-1" />}
            </div>
          </div>
          {onDismiss && (
            <CloseButton ref={closeButtonReference} className="m-l-2" onClick={onDismiss} />
          )}
        </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';
  }
}
