import * as React from 'react'
import * as PropTypes from 'prop-types'
import styled from 'styled-components'
import { Size, SizeMap } from '../../services/utilities'
import stringify from '../../services/stringify'

export type Variant = 'outline' | 'primary'

const MIN_HEIGHTS: SizeMap<string | number> = {
  small: '2.5rem',
  medium: '3rem',
  large: '4rem',
}

const Base = styled('button')({
  'appearance': 'none',
  'padding': '0 1rem',
  'font': 'inherit',
  'fontFamily': 'var(--font-family-base)',
  'textTransform': 'uppercase',
  'fontWeight': 'bolder',
  'borderRadius': '0.25rem',
  'placeContent': 'center',
  'position': 'relative',
  'cursor': 'pointer',
  'border': 0,
  'userSelect': 'none',
  'textDecoration': 'none',
  'transitionProperty': 'background-color, color',
  'whiteSpace': 'nowrap',
  'lineHeight': 1,
  ':focus': {
    '--color-accent': 'var(--color-active, Highlight)',
    'outline': 0,
  },
  ':disabled': {
    opacity: 0.5,
    cursor: 'not-allowed',
  },
  '::-moz-focus-inner': {
    outline: 0,
    border: 0,
  },
})

Base.displayName = 'button'

const Border = styled('span')({
  'borderColor': 'var(--color-accent, blue)',
  'boxSizing': 'border-box',
  'display': 'inline-block',
  'borderWidth': '0.125rem',
  'borderStyle': 'solid',
  'position': 'absolute',
  'top': 0,
  'left': 0,
  'width': '100%',
  'height': '100%',
  'borderRadius': 'inherit',
  'pointerEvents': 'none',
  'transitionProperty': 'border-color',
  '::before': {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    content: "''",
    borderRadius: '0.125rem',
    opacity: 0.5,
    pointerEvents: 'none',
  },
  [`${Base}:focus &::before`]: {
    boxShadow: '0 0 0 0.375rem var(--color-accent, blue)',
  },
})

Border.displayName = 'span'

const propTypes = {
  /**
   * Size of the component.
   */
  size: PropTypes.oneOf<Size>(['small', 'medium', 'large']),
  /**
   * Variant of the component.
   */
  variant: PropTypes.oneOf<Exclude<Variant, 'unknown'>>(['outline', 'primary']),
  /**
   * Should the component take up the remaining space parallel to the content flow?
   */
  block: PropTypes.bool,
  /**
   * Text to identify the action associated upon activation of the component.
   */
  children: PropTypes.any,
  /**
   * Can the component be activated?
   */
  disabled: PropTypes.bool,
}

type Props = PropTypes.InferProps<typeof propTypes>

const defaultVariantStyleSet: React.CSSProperties = {
  backgroundColor: 'transparent',
  color: 'var(--color-accent, blue)',
}

const variantStyleSets: Record<Variant, React.CSSProperties> = {
  outline: defaultVariantStyleSet,
  primary: {
    backgroundColor: 'var(--color-accent, blue)',
    color: 'var(--color-bg, white)',
  },
}

const Button = React.forwardRef<HTMLButtonElement, Props>(
  ({ size = 'medium', variant = 'outline', block = false, disabled = false, children, ...etcProps }, ref) => {
    const { [variant as Variant]: theVariantStyleSet = defaultVariantStyleSet } = variantStyleSets

    return (
      <Base
        {...etcProps}
        ref={ref}
        disabled={disabled!}
        style={{
          ...theVariantStyleSet,
          minHeight: MIN_HEIGHTS[size as Size],
          width: block ? '100%' : undefined,
          display: block ? 'grid' : 'inline-grid',
        }}
      >
        <Border />
        {stringify(children)}
      </Base>
    )
  },
)

Button.propTypes = propTypes

Button.displayName = 'Button'

export default Button
