import clsx from 'clsx';
import React, { forwardRef } from 'react';
import { useTranslation } from 'react-i18next';
import { twMerge } from 'tailwind-merge';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import ImageWithFallback from '@/components/image/ImageWithFallback';
import Link from '@/components/link/Link';
import { CloseIcon } from '@/icons';

type BaseProps = Pick<
  React.HTMLAttributes<HTMLDivElement>,
  'className' | 'style'
> & {
  title?: React.ReactNode;
  subtitle?: React.ReactNode;
  contentClassName?: string;
  closeButtonClassName?: string;
  imageClassName?: string;
  imageContainerClassName?: string;
  textClassName?: string;
  backgroundImage?: string;
  closeButtonLabel?: string;
  onCloseClick?: React.MouseEventHandler<HTMLButtonElement>;
  image?: React.ReactNode | React.ComponentType<{ className?: string }>;
  alt?: string;
};

export type Props = BaseProps &
  Omit<
    | React.ComponentProps<typeof Link>
    | (React.ButtonHTMLAttributes<HTMLButtonElement> &
        Required<
          Pick<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>
        > & { href?: never })
    | (React.HTMLAttributes<HTMLDivElement> & {
        href?: never;
        onClick?: never;
      }),
    keyof BaseProps
  >;

const Banner = forwardRef<
  HTMLAnchorElement | HTMLButtonElement | HTMLDivElement,
  Props
>((props, ref) => {
  const {
    title,
    subtitle,
    className: explicitClassName,
    closeButtonClassName: explicitCloseButtonClassName,
    contentClassName: explicitContentClassName,
    imageClassName: explicitImageClassName,
    imageContainerClassName: explicitImageContainerClassName,
    textClassName: explicitTextClassName,
    style: explicitStyle,
    children,
    image,
    alt,
    backgroundImage,
    closeButtonLabel,
    onCloseClick,
    ...restProps
  } = props;

  const { t } = useTranslation();

  const className = twMerge(
    clsx(
      'relative flex flex-row items-center',
      'rounded-lg @xl:rounded-full',
      'bg-background-tertiary text-foreground-primary',
      {
        'bg-(image:--banner-background-image) bg-cover bg-center':
          backgroundImage,
      }
    ),
    explicitClassName
  );

  const closeButtonClassName = twMerge(
    'mr-2 @sm:mr-4',
    explicitCloseButtonClassName
  );

  // The "content" inside the banner: image, icon, text, etc.
  const contentClassName = twMerge(
    'relative flex-1 flex flex-row items-center justify-center gap-3 @sm:gap-4 px-3 py-2 @sm:p-4 @xl:px-6',
    explicitContentClassName
  );

  const imageContainerClassName = twMerge(
    clsx('shrink-0 flex justify-center items-center', {
      'rounded-sm overflow-clip': !explicitImageContainerClassName,
      'rounded-full': !!explicitImageContainerClassName,
    }),
    explicitImageContainerClassName
  );

  const imageClassName = twMerge(
    clsx('text-current w-10 h-10 @sm:w-14 @sm:h-14', {
      'w-6 h-6 @sm:w-10 @sm:h-10 m-2': !!explicitImageContainerClassName,
    }),
    explicitImageClassName
  );

  const textClassName = twMerge(
    'flex-1 flex flex-col justify-center text-left min-h-14',
    explicitTextClassName
  );

  const style = backgroundImage
    ? ({
        '--banner-background-image': `url('${backgroundImage}')`,
        ...explicitStyle,
      } as React.CSSProperties)
    : explicitStyle;

  const innerContent = (
    <>
      {image && (
        <div className={imageContainerClassName}>
          {typeof image === 'function' ? (
            React.createElement(image, { className: imageClassName })
          ) : typeof image === 'string' ? (
            <ImageWithFallback
              src={image}
              alt={alt || ''}
              className={twMerge('object-contain', imageClassName)}
            />
          ) : (
            image
          )}
        </div>
      )}
      <div className={textClassName}>
        {title && (
          <div className='font-sans text-[14px] font-medium sm:text-[14px] lg:text-base'>
            {title}
          </div>
        )}
        {subtitle && (
          <div className='font-sans text-[12px] font-normal sm:text-xs md:text-sm lg:text-sm'>
            {subtitle}
          </div>
        )}
        {children}
      </div>
    </>
  );

  const content = restProps.href ? (
    <Link
      {...(restProps as React.ComponentProps<typeof Link>)}
      className={contentClassName}
      ref={ref as React.Ref<HTMLAnchorElement>}
    >
      {innerContent}
    </Link>
  ) : restProps.onClick ? (
    <button
      type='button'
      {...(restProps as React.ButtonHTMLAttributes<HTMLButtonElement>)}
      className={contentClassName}
      ref={ref as React.Ref<HTMLButtonElement>}
    >
      {innerContent}
    </button>
  ) : (
    <div
      {...(restProps as React.HTMLAttributes<HTMLDivElement>)}
      className={contentClassName}
      ref={ref as React.Ref<HTMLDivElement>}
    >
      {innerContent}
    </div>
  );

  return (
    <div className={className} style={style}>
      {content}
      {onCloseClick && (
        <Button
          variant={
            explicitCloseButtonClassName
              ? ButtonVariant.Inherit
              : ButtonVariant.Tertiary
          }
          shape={ButtonShape.Pill}
          size={ButtonSize.Small}
          className={closeButtonClassName}
          onClick={onCloseClick}
          icon={CloseIcon}
          aria-label={closeButtonLabel || t('cta.dismiss')}
        />
      )}
    </div>
  );
});
Banner.displayName = 'Banner';

export default Banner;
