// @flow
import React, { useState, useMemo, type ComponentType } from 'react';
import styled from 'styled-components';
import { v4 as guid } from 'uuid';
import type { CheckBoxProps } from './check-box.proptypes';
import {
  CheckBoxIcon,
  type CheckBoxIconProps,
} from '../internals/check-box-icon';
import {
  getBaseFocusState,
  focusedBoxShadow,
  focusedBorderShadow,
  focusedBorderColor,
  focusedBorderRadius,
} from '../../../mixins/focus';
import { getSpacingRules } from '../../../mixins/spacing';
import { getStackedRules } from '../../../mixins/stacking';
import { getStackeeRules } from '../../../mixins/stackee';
import { getPaletteColor } from '../../../mixins/branding';

const getCursor = ({ isDisabled }) => (isDisabled ? 'default' : 'pointer');

const CheckBoxContainer = styled.label`
  border-radius: ${focusedBorderRadius};
  color: ${({ isDisabled }) =>
    getPaletteColor(isDisabled ? 'greyscale-dark' : 'greyscale-black')};
  position: relative;

  ${getBaseFocusState({
    hoverTransition: false,
    activeTransition: false,
    pointerCursor: false,
  })}

  &.focus-visible-within {
    box-shadow: ${focusedBoxShadow},
      ${focusedBorderShadow} ${focusedBorderColor};
  }

  cursor: ${getCursor};

  ${getSpacingRules}
  ${getStackedRules}
  ${getStackeeRules}
`;

const LeftIcon = styled.div`
  display: flex;
  align-items: center;
  margin-left: -10px;
`;

const RightIcon = styled.div`
  display: flex;
  align-items: center;
  margin-right: -10px;
`;

const InvisibleInput = styled.input`
  height: 0;
  width: 0;
  margin: 0;
  padding: 0;
  opacity: 0;
  position: absolute;
`;

const StyledCheckBoxIcon: ComponentType<CheckBoxIconProps> = styled(
  CheckBoxIcon,
)`
  min-width: 44px;
`;

export const CheckBox = ({
  selected = false,
  onToggle,
  name,
  disabled = false,
  spacing = 'ph1',
  grow,
  shrink,
  basis,
  stacked = 'row',
  align = 'center',
  justify,
  wrap,
  children,
  direction = 'left',
  borderColor = 'greyscale-dark',
  onFocus = () => {},
  onBlur = () => {},
  className,
  id,
  'data-qa': dataQa,
  'data-cy': dataCy,
  ...props
}: CheckBoxProps) => {
  const [hasKeyboardFocus, setKeyboardFocus] = useState(false);

  // Hack to workaround the lack of :focus-visible-within polyfill support on Chrome.
  const handleFocus = (e) => {
    onFocus(e);
    setKeyboardFocus(e?.target?.className.includes('focus-visible'));
  };

  const handleBlur = (e) => {
    onBlur(e);
    setKeyboardFocus(false);
  };

  const leftContent = (
    <>
      <LeftIcon>
        <StyledCheckBoxIcon selected={selected} borderColor={borderColor} />
      </LeftIcon>
      {children}
    </>
  );

  const rightContent = (
    <>
      {children}
      <RightIcon>
        <StyledCheckBoxIcon selected={selected} borderColor={borderColor} />
      </RightIcon>
    </>
  );

  const labelId = useMemo(() => `checkbox-${guid()}`, []);

  return (
    <CheckBoxContainer
      spacing={spacing}
      {...{ grow, shrink, basis, stacked, align, justify, wrap }}
      className={
        hasKeyboardFocus ? `${className || ''} focus-visible-within` : className
      }
      isDisabled={disabled}
      id={labelId}
      tabIndex={-1}
    >
      <InvisibleInput
        type="checkbox"
        checked={selected}
        name={name}
        disabled={disabled}
        onChange={onToggle}
        onFocus={handleFocus}
        onBlur={handleBlur}
        id={id}
        data-cy={dataCy}
        data-qa={dataQa}
        aria-labelledby={labelId}
        {...props}
      />
      {direction === 'left' ? leftContent : rightContent}
    </CheckBoxContainer>
  );
};

CheckBox.displayName = 'CheckBox';
