import * as React from 'react'
import styled from 'styled-components'

// TODO implement web-client text inputs!

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

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

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

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

const SECONDARY_TEXT_SIZES: Record<TextInputSize, string | number> = {
  [TextInputSize.SMALL]: '0.65em',
  [TextInputSize.MEDIUM]: '0.75em',
  [TextInputSize.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,
  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 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 Input = styled('input')({
  display: 'block',
  paddingTop: 0,
  paddingBottom: 0,
  boxSizing: 'border-box',
  position: 'relative',
  border: 0,
  borderRadius: 'inherit',
  margin: 0,
  font: 'inherit',
  minHeight: '4rem',
  minWidth: '8rem',
  maxWidth: '100%',
  width: '100%',
  zIndex: 1,
  transitionProperty: 'background-color, color',
  ':focus': {
    outline: 0,
    color: 'var(--color-fg, black)',
  },
  ':disabled': {
    cursor: 'not-allowed',
  },
  '@media only screen': {
    backgroundColor: 'var(--color-bg, white)',
    color: 'var(--color-fg, black)',
  },
})

Input.displayName = 'input'

const TextArea = styled('textarea')({
  display: 'block',
  verticalAlign: 'top',
  width: '100%',
  boxSizing: 'border-box',
  position: 'relative',
  border: 0,
  borderRadius: 'inherit',
  margin: 0,
  font: 'inherit',
  minHeight: '4rem',
  minWidth: '3rem',
  maxWidth: '100%',
  zIndex: 1,
  transitionProperty: 'background-color, color',
  ':focus': {
    outline: 0,
  },
  '@media only screen': {
    backgroundColor: 'var(--color-bg, white)',
    color: 'var(--color-fg, black)',
  },
})

TextArea.displayName = 'textarea'

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

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

export type Props = Omit<React.HTMLProps<HTMLInputElement>, 'as' | 'className' | 'style' | 'placeholder' | 'size'> & {
  /**
   * 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?: TextInputSize,
  /**
   * Additional description, usually graphical, indicating the nature of the component's value.
   */
  indicator?: React.ReactNode,
  /**
   * Should 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 inputting textual values.
 *
 * This component supports multiline input and adjusts its layout accordingly.
 */
export const TextInput = React.forwardRef<HTMLInputElement, Props>(
  (
    {
      label = '',
      hint = '',
      indicator = null,
      size = TextInputSize.MEDIUM,
      disabled = false,
      border = false,
      alternate = false,
      block = false,
      ...etcProps
    },
    ref,
  ) => (
    <ComponentBase
      style={{
        display: block ? 'block' : 'inline-block',
        opacity: disabled ? 0.5 : undefined,
      }}
    >
      {border && <Border />}
      <CaptureArea>
        {label && (
          <>
            <LabelWrapper
              style={{
                paddingLeft: alternate ? (border ? '0.5rem' : undefined) : '0.5rem',
                paddingTop: alternate ? `calc(${LABEL_VERTICAL_PADDING_SIZES[size!]} * 0.25)` : LABEL_VERTICAL_PADDING_SIZES[size!],
                paddingBottom: LABEL_VERTICAL_PADDING_SIZES[size!],
                paddingRight: indicator ? MIN_HEIGHTS[size!] : (alternate ? (border ? '0.5rem' : undefined) : '0.5rem'),
                fontSize: SECONDARY_TEXT_SIZES[size!],
              }}
            >
              {label}
            </LabelWrapper>
            {' '}
          </>
        )}
        <Input
          {...etcProps}
          ref={ref as React.Ref<HTMLInputElement>}
          disabled={disabled!}
          style={{
            paddingLeft: alternate ? (border ? '0.5rem' : undefined) : '1rem',
            fontSize: INPUT_FONT_SIZES[size!],
            minHeight: MIN_HEIGHTS[size!],
            paddingTop: alternate ? `calc(${SECONDARY_TEXT_SIZES[size!]} * 2)`: undefined,
            paddingRight: indicator ? MIN_HEIGHTS[size!] : (alternate ? (border ? '0.5rem' : undefined) : '1rem'),
          }}
          aria-label={label}
        />
      </CaptureArea>
      {hint && (
        <>
          {' '}
          <HintWrapper
            style={{
              top: alternate ? SECONDARY_TEXT_SIZES[size!] : undefined,
              bottom: alternate ? undefined : 0,
              paddingLeft: alternate ? (border ? '0.5rem' : undefined) : '1rem',
              paddingTop: alternate ? undefined : LABEL_VERTICAL_PADDING_SIZES[size!],
              paddingBottom: alternate ? undefined : LABEL_VERTICAL_PADDING_SIZES[size!],
              paddingRight: indicator ? MIN_HEIGHTS[size!] : (alternate ? (border ? '0.5rem' : undefined) : '1rem'),
              fontSize: SECONDARY_TEXT_SIZES[size!],
              lineHeight: alternate ? 1.5 : 1,
            }}
          >
            {hint}
          </HintWrapper>
        </>
      )}
      {indicator && (
        <IndicatorWrapper
          style={{
            width: MIN_HEIGHTS[size!],
            height: MIN_HEIGHTS[size!],
          }}
        >
          {indicator}
        </IndicatorWrapper>
      )}
    </ComponentBase>
  ),
)

TextInput.displayName = 'TextInput'
