// @flow
import React, { type ComponentType } from 'react';
import styled from 'styled-components';
import MaskedInput from 'cleave.js/react';
import 'cleave.js/dist/addons/cleave-phone.ca';
import 'cleave.js/dist/addons/cleave-phone.gb';
import 'cleave.js/dist/addons/cleave-phone.us';
import { getFontFamily, getFontSize } from '../../../mixins/typography';
import { getPaletteColor } from '../../../mixins/branding';
import { Box, type BoxProps } from '../../../primitives/box';
import { RevealTextHide, RevealTextShow, Close } from '../../icon';
import {
  UnstyledButton,
  type UnstyledButtonProps,
} from '../../behaviors/unstyled-button';
import { floatingLabelInputStyles, FormBox } from '../internals/forms.common';
import type { LabelStyle } from './text-input.proptypes';

const getTextColor = ({ theme, error }) =>
  error != null && error !== false
    ? theme.colorPalette['error-fg']
    : theme.colorPalette['greyscale-black'];

const getInputPaddingVertical = ({ theme, labelStyle }) =>
  labelStyle === 'floating' || labelStyle === 'external-large'
    ? theme.spacing.s2
    : theme.spacing.s1;

const getInputPaddingRight = ({ theme }) => theme.spacing.s1;
const getInputPaddingLeft = ({ theme, isEmbeddable, type }) =>
  isEmbeddable || type === 'search' ? '0' : theme.spacing.s2;

// Setting the height as fontSize plus vertical padding allows consistent
// label float calculations with varying fontSize. We could do this more
// simply by setting margins instead of padding, but then the click hotspot
// for the internal DOM input is too small.
const getHeight = (props) =>
  `${
    Number.parseInt(getFontSize(props), 10) +
    Number.parseInt(getInputPaddingVertical(props), 10) * 2
  }px`;

const getFontWeight = ({ isEmbeddable, labelStyle }) =>
  isEmbeddable || labelStyle === 'external' ? 'normal' : 'bold';

// $FlowFixMe - TODO typedef this properly post TS migration.
export const Input: any = styled(MaskedInput).withConfig({
  shouldForwardProp: (prop) =>
    !['isEmbeddable', 'labelStyle', 'error'].includes(prop),
})`
  && {
    background: transparent;
    background-color: transparent;
    border: 0;
    color: ${getTextColor};
    font-family: ${getFontFamily};
    font-size: ${getFontSize};
    font-weight: ${getFontWeight};
    flex-grow: 1;
    line-height: 1;
    height: ${getHeight};
    outline: none;
    padding-top: ${getInputPaddingVertical};
    padding-right: ${getInputPaddingRight};
    padding-bottom: ${getInputPaddingVertical};
    padding-left: ${getInputPaddingLeft};
    transform: none;
    margin: 0px;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;

    &::placeholder {
      color: ${getPaletteColor('greyscale-dark')};
      font-style: italic;
      font-weight: normal;
      /* FF mucks with the opacity of the placeholder in some scenarios. */
      opacity: 1;
    }

    /* Turn of FF's "helpful" base styling for invalid inputs */
    :invalid {
      box-shadow: none;
    }

    ${floatingLabelInputStyles}
  }
`;

export const RelativeBox: ComponentType<BoxProps> = styled(Box)`
  position: relative;
`;

export type WrapperProps = {|
  isEmbeddable: boolean,
  disabled: boolean,
  readOnly: boolean,
  labelStyle: LabelStyle,
  ...BoxProps,
|};
export const Wrapper = ({
  isEmbeddable,
  disabled,
  readOnly,
  labelStyle,
  ...rest
}: WrapperProps) =>
  isEmbeddable ? (
    <Box {...rest} />
  ) : (
    <FormBox
      disabled={disabled}
      readOnly={readOnly}
      labelStyle={labelStyle}
      {...rest}
    />
  );

export type ShowHideButtonProps = {|
  textVisible: boolean,
  onToggle: () => void,
  hideLabel?: string,
  revealLabel?: string,
  disabled?: boolean,
|};
export const ShowHideButton = ({
  textVisible,
  onToggle,
  hideLabel,
  revealLabel,
  disabled = false,
}: ShowHideButtonProps) => (
  <Box stacked="row" align="stretch">
    <UnstyledButton
      stacked="row"
      align="center"
      onClick={onToggle}
      aria-label={textVisible ? hideLabel : revealLabel}
      disabled={disabled}
      spacing="p1"
    >
      {textVisible ? <RevealTextShow /> : <RevealTextHide />}
    </UnstyledButton>
  </Box>
);

export type ClearButtonProps = {| onClick: () => void, label?: string |};
export const ClearButton = ({ onClick, label }: ClearButtonProps) => {
  return (
    <ClearButtonHotSpot
      onClick={onClick}
      aria-label={label}
      stacked="row"
      align="center"
      justify="center"
      spacing="p1"
    >
      <Close colorName="greyscale-black" />
    </ClearButtonHotSpot>
  );
};

export const ClearButtonHotSpot: ComponentType<UnstyledButtonProps> = styled(
  UnstyledButton,
)`
  width: 44px;
`;

export const IconBox: ComponentType<{|
  ...BoxProps,
  labelStyle: LabelStyle,
|}> = styled(Box)`
  margin: ${({ labelStyle }) =>
    labelStyle === 'external' ? '-14px 0' : undefined};
`;
