// @flow
import React, { useState, useContext } from 'react';
import randomstring from 'randomstring';
import type { ComponentType } from 'react';
import { compose, getContext } from 'recompose';
import styled, { withTheme, css } from 'styled-components';
import { Box } from '../../../../primitives';
import { Text } from '../../../typography';
import type { SelectorItemProps } from './selector-item.proptypes';
import type { SelectorItemContext } from '../selector.proptypes';
import { selectorItemProptypes } from './selector-item.proptypes';
import { selectorContextTypes } from '../selector.proptypes';
import { RadioOption } from '../../../icon/radio-option';
import { Checkbox } from '../../../icon/checkbox';
import { SizeContext } from '../index';
import {
  getDisabledLabelTextColor,
  getPrimaryLabelFontSize,
  getPrimaryLabelFontFamily,
  getBoxPadding,
  getPrimaryLabelFontWeight,
} from './selector-item.css-rules';
import { getPaletteColor } from '../../../../mixins/branding';
import {
  getBaseFocusState,
  focusedBoxShadow,
  focusedBorderShadow,
  focusedBorderColor,
} from '../../../../mixins/focus';
import { UnstyledButton } from '../../../behaviors/unstyled-button';

const withSelectorContext = getContext(selectorContextTypes);

const handleMouseDown = event => {
  event.preventDefault();
  if (document.activeElement) {
    document.activeElement.blur();
  }
};

const isSmallSvgCss = css`
  svg {
    width: 32px;
    height: 32px;
  }
`;

const InputContainer = styled.div`
  position: absolute;
  display: flex;
  align-items: center;
  right: 10px;
  margin: 0;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

  ${getBaseFocusState({ hoverTransition: false, activeTransition: false })}
  &:focus-within {
    border-top: inherit;
    border-bottom: inherit;
    border-radius: inherit;
    box-shadow: ${focusedBoxShadow},
      ${focusedBorderShadow} ${focusedBorderColor};
  }

  ${({ isSmall }) => (isSmall ? isSmallSvgCss : '')}
`;

const GenericInput = styled.input`
  opacity: 0;
  width: 100%;
  height: 100%;
  &:not([disabled]):hover {
    cursor: pointer;
  }
`;

const optionStyling = css`
  position: absolute;
  right: 10px;
  top: 0;
  bottom: 0;
  margin: auto;
  pointer-events: none;
`;

const StyledRadioOption = styled(RadioOption)`
  ${optionStyling}
`;

const StyledCheckbox = styled(Checkbox)`
  ${optionStyling}
`;

const SelectorItemRow = styled(UnstyledButton)`
  width: 100%;
  margin: 0;
  align-items: center;
  display: flex;
  flex-grow: 1;
  justify-content: space-between;
  position: relative;
  background: ${getPaletteColor('white')};
`;

const BoxFlex = styled(Box)`
  flex: 1;
  padding: ${getBoxPadding};
  z-index: 1;
`;

const LabelWrapper = styled.div`
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  min-height: 24px;
  padding-right: 44px;
  justify-content: space-between;
  margin: ${({ isSmall, theme }) =>
    isSmall ? `5px 0` : `${theme.spacing.s1} 0`};
`;

const LabelText = styled(Text)`
  margin: 0;

  ${({ disabled }) =>
    disabled &&
    css`
      color: ${getDisabledLabelTextColor};
    `}
`;

const PrimaryLabelText = styled(LabelText)`
  font-size: ${getPrimaryLabelFontSize};
  font-family: ${getPrimaryLabelFontFamily};
  font-weight: ${getPrimaryLabelFontWeight};
`;

/**
 * Depending on if this is a <Selector type="radio"> (default) or
 * <Selector type="checkbox"> then we manage the value differently
 */
const getCheckedState = (
  type?: string,
  value: string | number,
  managedValue?: string | number | string[],
) => {
  if (type === 'checkbox' && Array.isArray(managedValue)) {
    return managedValue.includes(value);
  }

  return value === managedValue || String(value) === String(managedValue);
};

export const _SelectorItem = ({
  id,
  value,
  managedValue,
  label,
  secondaryLabel,
  disabled,
  large,
  children,
  name = '',
  onChange,
  checked,
  stateful = false,
  type = 'radio',
  onRef = _ => {},
  childElementSpacing = ['mr2'],
}: SelectorItemProps & SelectorItemContext) => {
  const size = useContext(SizeContext);
  const isLarge = large || size === 'lg'; // TODO: deprecate the large prop
  const isSmall = size === 'sm';

  const [selectorItemRef, setSelectorItemRef] = useState(null);

  const elementId = `SelectorItem-${id}-${randomstring.generate(5)}`;
  const generatedName = `Selector-field--${name}-${randomstring.generate(5)}`;
  const isChecked = stateful
    ? getCheckedState(type, value, managedValue)
    : checked;

  const childElement =
    children != null ? (
      <Box spacing={childElementSpacing}>{children}</Box>
    ) : null;

  const secondaryLabelElement =
    secondaryLabel != null ? (
      <LabelText disabled={disabled} fontSize="f1" spacing="m0">
        {secondaryLabel}
      </LabelText>
    ) : null;

  const labelElement =
    label != null ? (
      <LabelWrapper isSmall={isSmall}>
        <PrimaryLabelText disabled={disabled} large={isLarge}>
          {label}
        </PrimaryLabelText>
        {secondaryLabelElement}
      </LabelWrapper>
    ) : null;

  return (
    <SelectorItemRow
      htmlFor={elementId}
      tabIndex="-1"
      onClick={() => {
        if (selectorItemRef != null) {
          selectorItemRef.click();
        }
      }}
    >
      <BoxFlex stacked="column" large={isLarge}>
        {labelElement}
        {childElement}
      </BoxFlex>
      <InputContainer isSmall={isSmall}>
        <GenericInput
          ref={r => {
            setSelectorItemRef(r);
            onRef(r);
          }}
          id={elementId}
          name={generatedName}
          type={type}
          onChange={onChange}
          checked={isChecked}
          disabled={disabled}
          value={value}
          onMouseDown={handleMouseDown}
        />
        {type === 'radio' ? (
          <StyledRadioOption id={elementId} checked={isChecked} />
        ) : (
          <StyledCheckbox id={elementId} selected={isChecked} />
        )}
      </InputContainer>
    </SelectorItemRow>
  );
};

export const SelectorItem: ComponentType<SelectorItemProps> = compose(
  withSelectorContext,
  withTheme,
)(_SelectorItem);

SelectorItem.displayName = 'SelectorItem';
SelectorItem.propTypes = selectorItemProptypes;
