import * as React from 'react'
import styled from 'styled-components'
import Icon from '../Icon'

export enum SelectSize {
  SMALL = 'small',
  MEDIUM = 'medium',
  LARGE = 'large',
}

const MIN_HEIGHTS: Record<SelectSize, string | number> = {
  [SelectSize.SMALL]: '2.5rem',
  [SelectSize.MEDIUM]: '3rem',
  [SelectSize.LARGE]: '4rem',
}

const LABEL_VERTICAL_PADDING_SIZES: Record<SelectSize, string | number> = {
  [SelectSize.SMALL]: '0.125rem',
  [SelectSize.MEDIUM]: '0.25rem',
  [SelectSize.LARGE]: '0.5rem',
}

const VERTICAL_PADDING_SIZES: Record<SelectSize, string | number> = {
  [SelectSize.SMALL]: '0.6rem',
  [SelectSize.MEDIUM]: '0.85rem',
  [SelectSize.LARGE]: '1.25rem',
}

const INPUT_FONT_SIZES: Record<SelectSize, string | number> = {
  [SelectSize.SMALL]: '0.85em',
  [SelectSize.MEDIUM]: '0.85em',
  [SelectSize.LARGE]: '1em',
}

const SECONDARY_TEXT_SIZES: Record<SelectSize, string | number> = {
  [SelectSize.SMALL]: '0.65em',
  [SelectSize.MEDIUM]: '0.75em',
  [SelectSize.LARGE]: '0.85em',
}

const ComponentBase = styled('div')({
  verticalAlign: 'middle',
  position: 'relative',
  borderRadius: '0.25rem',
  fontFamily: 'var(--font-family-base, sans-serif)',
  maxWidth: '100%',
  ':focus-within': {
    '--color-accent': 'var(--color-active, Highlight)',
  },
  'fieldset[disabled] &': {
    opacity: 0.5,
  },
})

ComponentBase.displayName = 'div'

const CaptureArea = styled('label')({
  display: 'block',
  borderRadius: 'inherit',
  overflow: 'hidden',
})

CaptureArea.displayName = 'label'

const LabelWrapper = styled('span')({
  color: 'var(--color-accent, blue)',
  boxSizing: 'border-box',
  position: 'absolute',
  top: 0,
  left: 0,
  paddingLeft: '0.5rem',
  fontSize: '0.85em',
  maxWidth: '100%',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  whiteSpace: 'nowrap',
  fontWeight: 'bolder',
  zIndex: 2,
  pointerEvents: 'none',
  transitionProperty: 'color',
  lineHeight: 1,
  userSelect: 'none',
})

LabelWrapper.displayName = 'span'

const Input = styled('select')({
  display: 'block',
  appearance: 'none',
  boxSizing: 'border-box',
  position: 'relative',
  border: 0,
  paddingLeft: '1rem',
  margin: 0,
  font: 'inherit',
  minHeight: '4rem',
  minWidth: '3rem',
  maxWidth: '100%',
  width: '100%',
  zIndex: 1,
  cursor: 'pointer',
  transitionProperty: 'background-color, color',
  ':focus': {
    outline: 0,
  },
  ':disabled': {
    cursor: 'not-allowed',
  },
  '::-moz-focus-inner': {
    outline: 0,
    border: 0,
  },
  '@media only screen': {
    backgroundColor: 'var(--color-bg, white)',
    color: 'var(--color-fg, black)',
  },
})

Input.displayName = 'select'

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',
  zIndex: 2,
  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',
  },
  [`${ComponentBase}:focus-within &::before`]: {
    boxShadow: '0 0 0 0.375rem var(--color-accent, blue)',
  },
})

const HintWrapper = styled('span')({
  boxSizing: 'border-box',
  position: 'absolute',
  bottom: 0,
  left: 0,
  paddingLeft: '1rem',
  fontSize: '0.85em',
  opacity: 0.5,
  maxWidth: '100%',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  whiteSpace: 'nowrap',
  zIndex: 2,
  pointerEvents: 'none',
  lineHeight: 1,
  userSelect: 'none',
})

const IndicatorWrapper = styled('span')({
  color: 'var(--color-accent, blue)',
  boxSizing: 'border-box',
  position: 'absolute',
  top: 0,
  right: 0,
  height: '100%',
  display: 'grid',
  placeContent: 'center',
  padding: '0 1rem',
  zIndex: 1,
  pointerEvents: 'none',
  transitionProperty: 'color',
  lineHeight: 1,
  userSelect: 'none',
})

export type Props = Omit<React.HTMLProps<HTMLSelectElement>, 'as' | 'className' | 'style' | 'size' | 'rows'> & {
  /**
   * Short textual description indicating the nature of the component's value.
   */
  label?: React.ReactNode,
  /**
   * Short textual description as guidelines for valid input values.
   */
  hint?: React.ReactNode,
  /**
   * Size of the component.
   */
  size?: SelectSize,
  /**
   * Number of rows to display.
   */
  rows?: number,
  /**
   * Does the component display a border?
   */
  border: boolean,
  /**
   * Should the component be displayed with an alternate appearance?
   */
  alternate?: boolean,
  /**
   * Should the component occupy the whole width of its parent?
   */
  block?: boolean,
}

/**
 * Component for selecting values from a larger number of options.
 */
export const Select = React.forwardRef<HTMLSelectElement, Props>(
  (
    {
      label = '',
      hint = '',
      size = SelectSize.MEDIUM,
      rows,
      border = false,
      multiple = false,
      disabled = false,
      block = false,
      ...etcProps
    },
    ref,
  ) => {
    return (
      <ComponentBase
        style={{
          display: block ? 'block' : 'inline-block',
          opacity: disabled ? 0.5 : undefined,
        }}
      >
        {border && <Border />}
        <CaptureArea>
          {label && (
            <>
              <LabelWrapper
                style={{
                  paddingTop: LABEL_VERTICAL_PADDING_SIZES[size!],
                  paddingBottom: LABEL_VERTICAL_PADDING_SIZES[size!],
                  paddingRight: !multiple ? MIN_HEIGHTS[size!] : '0.5rem',
                  fontSize: SECONDARY_TEXT_SIZES[size!],
                }}
              >
                {label}
              </LabelWrapper>
              {' '}
            </>
          )}
          <Input
            {...etcProps}
            ref={ref}
            disabled={disabled}
            multiple={multiple}
            size={rows}
            style={{
              verticalAlign: 'top',
              fontSize: INPUT_FONT_SIZES[size!],
              height: multiple ? undefined : MIN_HEIGHTS[size!],
              minHeight: MIN_HEIGHTS[size!],
              resize: multiple ? 'vertical' : undefined,
              paddingTop: VERTICAL_PADDING_SIZES[size!],
              paddingBottom: VERTICAL_PADDING_SIZES[size!],
              paddingRight: !multiple ? MIN_HEIGHTS[size!] : '1rem',
            }}
            aria-label={label}
          />
        </CaptureArea>
        {hint && (
          <>
            {' '}
            <HintWrapper
              style={{
                paddingTop: LABEL_VERTICAL_PADDING_SIZES[size!],
                paddingBottom: LABEL_VERTICAL_PADDING_SIZES[size!],
                paddingRight: MIN_HEIGHTS[size!],
                fontSize: SECONDARY_TEXT_SIZES[size!],
              }}
            >
              {hint}
            </HintWrapper>
          </>
        )}
        {!multiple && (
          <IndicatorWrapper
            style={{
              width: MIN_HEIGHTS[size!],
            }}
          >
            <Icon name="chevron-down" label="" />
          </IndicatorWrapper>
        )}
      </ComponentBase>
    )
  },
)

Select.displayName = 'Select'
