import React, { ReactElement } from 'react';
import css from '../../utils/css';
import Icon, { IconName } from '../Icon';
import Button from '../Button';
import {
  StyledBanner,
  StyledInner,
  StyledTitle,
  StyledContent,
  StyledIconWrapper,
  StyledCloseButton,
} from './StyledBanner';

import { CommonProps } from '../common';
import { getOrElse, map, none, Option, some } from '../../fp/Option';
import { Either, left, match, right } from '../../fp/Either';
import { pipe } from '../../fp/function';

export interface BannerProps extends CommonProps {
  /**
   * Banner content.
   */
  content: string | ReactElement;
  /**
   * Icon name or a react element as custom icon.
   * - undefined: use default icon according to Banner intent.
   * - null: no icon at all.
   * - string: an icon string from hero-design icon list.
   * - ReactElement: Custom icon by your own.
   */
  icon?: null | IconName | ReactElement;
  /**
   * Visual intent color to apply to banner.
   */
  intent?: 'success' | 'info' | 'warning' | 'danger' | 'error';
  /**
   * Closing callback. When onClose is available, an `x` button will be rendered on the right side of Banner. The callback will be called when user clicks on `x` button.
   */
  onClose?: () => void;
  /**
   * Banner title.
   */
  title?: string;
}

type BannerSize = 'default' | 'compact';

const ICON_MAP = {
  success: 'circle-ok',
  info: 'circle-info',
  warning: 'circle-warning',
  danger: 'circle-cancel',
  error: 'circle-cancel',
} as const;

const ICON_SIZE_MAP = {
  default: 'xlarge',
  compact: 'small',
} as const;

export const getBannerIcon = (
  defaultIcon: IconName,
  icon: undefined | null | IconName | ReactElement
): Option<Either<IconName, ReactElement>> => {
  if (icon === undefined) {
    return some(left(defaultIcon));
  }

  if (typeof icon === 'string' && Icon.List.includes(icon)) {
    return some(left(icon));
  }

  if (React.isValidElement(icon)) {
    return some(right(icon));
  }

  return none;
};

const Banner = ({
  intent = 'info',
  title,
  content,
  icon,
  onClose,
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
}: BannerProps): ReactElement => {
  const size: BannerSize = title !== undefined ? 'default' : 'compact';

  const defaultIcon = ICON_MAP[intent];
  const maybeIcon = getBannerIcon(defaultIcon, icon);
  const iconSize = ICON_SIZE_MAP[size];

  const renderWithIconWrapper = (iconEle: ReactElement): ReactElement => (
    <StyledIconWrapper
      themeIntent={intent}
      themeSize={size}
      data-test-id="banner-icon"
    >
      {iconEle}
    </StyledIconWrapper>
  );

  return (
    <StyledBanner
      themeSize={size}
      themeIntent={intent}
      id={id}
      className={className}
      style={{ ...style, ...css(sx) }}
      data-test-id={dataTestId}
    >
      {pipe(
        maybeIcon,
        map(
          match(
            (iconString): ReactElement =>
              renderWithIconWrapper(<Icon icon={iconString} size={iconSize} />),
            renderWithIconWrapper
          )
        ),
        getOrElse(() => null)
      )}
      <StyledInner>
        {title !== undefined && <StyledTitle>{title}</StyledTitle>}
        <StyledContent>{content}</StyledContent>
      </StyledInner>
      {onClose !== undefined && (
        <StyledCloseButton themeSize={size}>
          <Button.Icon
            icon="cancel"
            intent="inherit"
            onClick={onClose}
            data-test-id="close-button"
          />
        </StyledCloseButton>
      )}
    </StyledBanner>
  );
};

export default Banner;
