import { memo, useMemo } from 'react';
import classNames from 'classnames';
import css from 'styled-jsx/css';

import type { ServiceTypes } from '~/lib/types';
import type { ButtonProps } from '~/src/components/Button';
import type { Icon } from '~/src/components/Icon/toIcon';
import type { ServiceDisplayData } from '~/src/lib/getServiceDisplayData';
import type { CSSProperties } from 'react';

import Button from '~/src/components/Button';
import LinkIcon from '~/src/components/Icon/LinkIcon';
import getServiceDisplayData from '~/src/lib/getServiceDisplayData';
import { usePageTheme } from '../../hooks/theme';

export const DEFAULT_HEIGHT_REM = 5.5;

export interface ServiceButtonProps
  extends Pick<
    ButtonProps,
    | 'onClick'
    | 'isLoading'
    | 'isDisabled'
    | 'testId'
    | 'margin'
    | 'height'
    | 'tabIndex'
    | 'href'
    | 'data'
  > {
  className?: string;

  /**
   * The button text
   */
  text?: string;

  /**
   * Explicitly define an `Icon` to use.
   */
  Icon?: Icon;

  /**
   * Always show a colored icon instead of just on hover.
   *
   * @default false
   */
  withColoredIcon?: boolean;

  /**
   * Define whether an `href` should be used on the button.
   * When in edit-mode we don't want to actually have `hrefs`.
   *
   * @default true
   */
  withHref?: boolean;
  renderAfter?: ButtonProps['renderAfter'];

  /**
   * The Service to infer text, color and icon from. This
   * isn't required as not all links/buttons map to a
   * built-in service.
   */
  serviceType?: ServiceTypes;

  /**
   * Override button styles
   */
  style?: CSSProperties;
}

const DEFAULT_SERVICE: Omit<ServiceDisplayData, 'urlPattern'> = {
  name: '',
  Icon: LinkIcon,
  color: '#000',
};

/**
 * A button component that takes care of resolving styling based on the given,
 * `serviceType`. When no `serviceType` is given or you want to override the
 * default service name or Icon, you can provide props to overridde.
 */
const ServiceButton = memo<ServiceButtonProps>((props) => {
  const {
    height = `${DEFAULT_HEIGHT_REM}rem`,
    text,
    className,
    onClick,
    withColoredIcon,
    withHref = true,
    href,
    renderAfter,
    Icon: CustomIcon,
    serviceType,
    testId,
    style,
    ...buttonProps
  } = props;

  const { buttonStyle, fontFamily } = usePageTheme();
  const service = getServiceDisplayData(serviceType) || DEFAULT_SERVICE;
  const buttonText = text || service?.name;

  const textColor = style?.color || buttonStyle.color;
  const backgroundColor = style?.backgroundColor || buttonStyle.backgroundColor;

  const iconColor = service?.color;
  const Icon = CustomIcon || service?.Icon;
  const defaultIconColor = withColoredIcon ? iconColor : undefined;

  // COMPLEX: If the `dynamicColoring` flag is set this is a BitmapIcon/CustomBrand that has opted-in
  // to 'dynamic coloring'. When the the icon isn't colored all the time (via `withColoredIcon`)
  // then we set it to 'dark' as the button background is white.
  const coloring = Icon.dynamicColoring && !withColoredIcon && 'dark';

  // when the icon is not de-saturated we lower the opacity slightly and
  // when it's hovered we bump it to 1 so that it 'pops' a little
  const defaultIconOpacity =
    withColoredIcon || (Icon.isBitmap && !Icon.dynamicColoring)
      ? 0.8
      : undefined;

  const styles = useMemo(
    () => css.resolve`
      .serviceButton :global(svg.serviceIcon) {
        color: ${textColor || '#000'} !important;
      }

      .serviceButton:focus-visible :global(svg.serviceIcon),
      .serviceButton:hover :global(svg.serviceIcon) {
        ${textColor
          ? ''
          : `
            color: currentColor !important;
            opacity: 1 !important;
          `}
      }

      .serviceButton:focus-visible :global(.bitmapIcon),
      .serviceButton:hover :global(.bitmapIcon) {
        ${textColor
          ? ''
          : `
            filter: none;
            opacity: 1 !important;
          `}
      }

      .withColoredIcon :global(.bitmapIcon) {
        filter: none;
      }
    `,
    [textColor]
  );

  // don't render anything if no button text resolved
  if (!buttonText) return null;

  return (
    <>
      <Button
        text={buttonText}
        withBeforeHoverStyle={false}
        href={withHref ? href : undefined}
        testId={`serviceButton ${serviceType || ''} ${testId || ''}`}
        className={classNames(className, 'serviceButton', styles.className, {
          withColoredIcon,
        })}
        height={height}
        onClick={onClick}
        renderAfter={renderAfter}
        isUppercase={false}
        textProps={{
          family: fontFamily,
          weight: 'regular',
          letterSpacing: 0.125,
        }}
        isNoFollow
        inNewTab
        withChevron
        style={{
          ...buttonStyle,
          ...style,

          padding: '0 2.2rem 0 3.2rem',

          color: textColor || '#000',
          backgroundColor: backgroundColor || '#fff',
        }}
        renderBefore={() => (
          <div style={{ color: iconColor }}>
            <Icon
              className="serviceIcon"
              color={defaultIconColor}
              opacity={defaultIconOpacity}
              coloring={coloring}
              size="0.5em"
              margin="0 0 0 0.02em"
            />
          </div>
        )}
        {...buttonProps}
      />
      {styles.styles}
    </>
  );
});

export default ServiceButton;
