import * as React from 'react';

import {
  ExtractProps,
  cornerShapeStencil,
  createContainer,
  focusRing,
} from '@workday/canvas-kit-react/common';
import {systemIconStencil} from '@workday/canvas-kit-react/icon';
import {Flex, mergeStyles} from '@workday/canvas-kit-react/layout';
import {colorSpace, createStencil, cssVar, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

import {BannerActionText} from './BannerActionText';
import {BannerIcon} from './BannerIcon';
import {BannerLabel} from './BannerLabel';
import {useBannerModel} from './hooks';

export interface BannerProps extends ExtractProps<typeof Flex, never> {
  /**
   * Children of the Banner. Should contain a `<Banner.Label>` a <Banner.Icon> and an optional `<Banner.ActionText>`
   */
  children?: React.ReactNode;
}

export const bannerStencil = createStencil({
  extends: cornerShapeStencil,
  base: {
    // TODO: Need to update fontFamily token [#3221](https://github.com/Workday/canvas-kit/issues/3221).
    fontFamily: `${system.fontFamily.default}, Helvetica Neue, Helvetica, Arial, sans-serif`,
    fontWeight: system.fontWeight.bold,
    lineHeight: system.legacy.lineHeight.subtext.lg,
    fontSize: system.legacy.fontSize.subtext.lg,
    letterSpacing: system.legacy.letterSpacing.subtext.lg,
    paddingInline: `${system.legacy.padding.sm} ${system.legacy.padding.md}`,
    paddingBlock: px2rem(10),
    border: '0',
    display: 'flex',
    alignItems: 'center',
    textAlign: 'left',
    [cornerShapeStencil.vars.shape]: system.legacy.shape.lg,
    gap: system.legacy.gap.sm,
    cursor: 'pointer',
    transition: 'background-color 120ms linear',
    outline: `${system.legacy.gap.xs} solid transparent`,
    boxShadow: system.depth[5],
    '&:focus-visible, &.focus': {
      outline: `${system.legacy.gap.xs} double transparent`,
      ...focusRing({separation: 2, outerColor: system.legacy.color.brand.border.primary}),
    },
  },
  modifiers: {
    hasErrors: {
      true: {
        backgroundColor: system.legacy.color.brand.accent.critical,
        color: system.color.fg.inverse,
        '&:hover, &.hover': {
          background: colorSpace.hover({
            color: system.legacy.color.brand.accent.critical,
            fallback: system.legacy.color.brand.accent.critical,
            colorType: 'accent',
          }),
        },
        '& [data-part="exclamation-circle-icon"]': {
          [systemIconStencil.vars.accentColor]: system.legacy.color.brand.accent.critical,
          [systemIconStencil.vars.color]: system.color.fg.inverse,
          [systemIconStencil.vars.backgroundColor]: system.color.fg.inverse,
        },
      },
      false: {
        backgroundColor: system.legacy.color.brand.accent.caution,
        color: system.color.fg.contrast.default,
        '&:hover, &.hover': {
          background: colorSpace.hover({
            color: system.legacy.color.brand.accent.caution,
            fallback: system.legacy.color.brand.accent.caution,
            colorType: 'accent',
          }),
        },
        '& [data-part="exclamation-triangle-icon"]': {
          [systemIconStencil.vars.accentColor]: cssVar(system.color.fg.inverse, 'currentColor'),
          [systemIconStencil.vars.color]: system.legacy.color.brand.fg.caution.strong,
          [systemIconStencil.vars.backgroundColor]: system.legacy.color.brand.fg.caution.strong,
        },
      },
    },
    isSticky: {
      true: {
        width: px2rem(222),
        borderStartEndRadius: 0,
        borderEndEndRadius: 0,
      },
      false: {
        width: px2rem(328),
      },
    },
  },
});

/**
 * `Banner` is a container component rendered as a `<button>` element that is responsible for creating
 * a `BannerModel` and sharing it with its subcomponents using React context.
 *
 * ```tsx
 * <Banner
 * 	isSticky={true}
 * 	hasError={true}
 * 	id='custom-banner-id'
 * 	onClick={() => console.log('clicked banner')}
 * >
 *   {Child components}
 * </Banner>
 * ```
 *
 * Alternatively, you may pass in a model using the hoisted model pattern.
 *
 * ```tsx
 * const model = useBannerModel({
 *   isSticky: true,
 *   hasError: true,
 *   id: 'custom-banner-id',
 * });
 *
 * return (
 *   <Banner onClick={() => console.log('clicked banner')} model={model}>
 *     {Child components}
 *   </Banner>
 * );
 * ```
 */
export const Banner = createContainer('button')({
  displayName: 'Banner',
  modelHook: useBannerModel,
  subComponents: {
    /**
     * `Banner.Icon` is a styled {@link SystemIcon}. The icon defaults to exclamationTriangleIcon or
     * exclamationCircleIcon when the model's hasError is true.
     *
     * ```tsx
     * <Banner.Icon />
     * ```
     */
    Icon: BannerIcon,
    /**
     * `Banner.Label` is a div element with flex styles. This component will get an id that will be used for
     * the aria-describedby on the top level `<button>`.
     *
     * ```tsx
     * <Banner.Label>3 Warnings</Banner.Label>
     * ```
     */
    Label: BannerLabel,
    /**
     * `Banner.ActionText` is a span element. This component will get an id that will be used
     * for the aria-labelledby on the top level `<button>`. This component will be visually hidden
     * when the model's `isSticky` prop is set to true.
     *
     * ```tsx
     * <Banner.ActionText>Custom call to action</Banner.ActionText>
     * ```
     */
    ActionText: BannerActionText,
  },
})<BannerProps>(({children, ...elemProps}, Element, model) => {
  return (
    <Element
      {...mergeStyles(
        elemProps,
        bannerStencil({
          hasErrors: model.state.hasError ? 'true' : 'false',
          isSticky: model.state.isSticky ? 'true' : 'false',
        })
      )}
    >
      {children}
    </Element>
  );
});
