import * as React from 'react'
import * as PropTypes from 'prop-types'
import * as FeatherIcon from 'react-feather'
import styled from 'styled-components'
import { pascalCase, pascalCaseTransformMerge } from 'pascal-case'
import splitValueAndUnit from '../../services/splitValueAndUnit'

const Label = styled('span')({
  'position': 'absolute',
  'left': -999999,
  'width': 1,
  'height': 1,
  ':empty': {
    display: 'none',
  },
})

const StyledIcon = styled('svg')({
  stroke: 'currentColor',
  strokeLinecap: 'round',
  display: 'inline-block',
  verticalAlign: 'middle',
})

const propTypes = {
  /**
   * Name of the icon to display.
   */
  name: PropTypes.string.isRequired,
  /**
   * Width of the icon's strokes.
   */
  weight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Size of the icon. This controls both the width and the height.
   */
  size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * CSS style of the icon. For icon dimensions, use `size` instead.
   */
  style: PropTypes.object,
  /**
   * Describe of what the component represents.
   */
  label: PropTypes.string,
  /**
   * Class name used for styling.
   */
  className: PropTypes.string,
}

type Props = PropTypes.InferProps<typeof propTypes>

/**
 * Component for displaying graphical icons.
 *
 * @see {@link //feathericons.com|Feather Icons} for a complete list of icons.
 * @param {string} name - Name of the icon to display.
 * @param {string} weight - Width of the icon's strokes.
 * @param {string | number} [size] - Size of the icon. This controls both the width and the height.
 * @param {CSSProperties} [style] - CSS style of the icon. For icon dimensions, use `size` instead.
 * @param {string} [label] - Describe of what the component represents.
 * @param {string} [className] - Class name used for styling.
 * @param {object} etcProps - The rest of the props.
 * @returns {React.ReactElement | null} - The component elements.
 */
const Icon: React.FC<Props> = ({
  name,
  weight = '0.125rem',
  size = '1.5rem',
  style = {},
  label = name,
  className = '',
  ...etcProps
}) => {
  const iconName = pascalCase(name, { transform: pascalCaseTransformMerge })
  const { [iconName as keyof typeof FeatherIcon]: TheIcon = null } = FeatherIcon
  const { magnitude: sizeValue, unit: sizeUnit } = splitValueAndUnit(size)
  const { magnitude: weightValue } = splitValueAndUnit(weight)
  const factor = weightValue * (3 / 2)

  if (TheIcon !== null) {
    return (
      <span {...etcProps} style={style!}>
        <StyledIcon
          className={className!}
          as={TheIcon}
          size={undefined}
          color={undefined}
          strokeLinecap={undefined}
          strokeWidth={undefined}
          style={{
            width: size!,
            height: size!,
            strokeWidth: `${factor / sizeValue}${sizeUnit}`,
          }}
          aria-label={label!}
        />
        <Label aria-hidden="true">{label}</Label>
      </span>
    )
  }

  return null
}

Icon.propTypes = propTypes

Icon.displayName = 'Icon'

export default Icon
