import React, { FocusEvent, MouseEvent, useState, useContext } from "react";
import classnames from "classnames";
import { useId } from "@reach/auto-id";
import { Box } from "../Box";
import { Icon, ICON_TYPE } from "../Icon";
import { Label } from "../Label";
import { Text } from "../Text";
import { Input, InputProps } from "../Input";
import { Stack, StackProps } from "../Stack";
import { ORIENTATION } from "../../types";
import { bem } from "../../utilities/bem";
import isUndefined from "../../utilities/isUndefined";
import { FormGroupContext } from "../FormGroup/FormGroupContext";

const cn = "Checkbox";

export interface CheckboxProps extends InputProps {
  label?: string;
  /**
   * Hides required styling on Label if it is within a FormGroupContext with `isRequired` equal to true
   */
  hideRequiredStyles?: boolean;
  /**
   * Shows required styling on Label
   */
  isRequired?: boolean;
}

const CheckboxGroup = (props: StackProps) => {
  const { orientation = ORIENTATION.VERTICAL, ...rest } = props;

  return <Stack orientation={orientation} {...rest} />;
};

export const Checkbox = ({
  checked,
  children,
  className,
  disabled = false,
  id: idProp,
  label,
  onBlur,
  onChange = () => {},
  onClick,
  onFocus,
  hideRequiredStyles = true,
  isRequired = undefined,
  tabIndex,
  theme: themeProp,
  ...rest
}: CheckboxProps) => {
  const [hasFocus, setHasFocus] = useState(false);
  const { isRequired: isRequiredContext, theme: themeContext } = useContext(
    FormGroupContext,
  );
  const fallbackId = `checkbox:${useId()}`;
  const id = isUndefined(idProp) ? fallbackId : idProp;
  const theme = themeProp || themeContext;

  const classes = classnames([
    bem(cn),
    { "is-disabled": disabled },
    { "is-focused": hasFocus },
    className,
  ]);

  const handleClick = (event: MouseEvent<HTMLInputElement>): void => {
    if (!disabled && onClick) {
      onClick(event);
    }
  };

  const handleFocus = (event: FocusEvent<HTMLInputElement>): void => {
    setHasFocus(true);
    if (onFocus) {
      onFocus(event);
    }
  };

  const handleBlur = (event: FocusEvent<HTMLInputElement>): void => {
    setHasFocus(false);
    if (onBlur) {
      onBlur(event);
    }
  };

  let calculatedRequired = false;
  if (isRequired !== undefined) {
    calculatedRequired = isRequired;
  } else if (isRequiredContext !== undefined) {
    calculatedRequired = isRequiredContext;
  }

  const bareCheckbox = (
    <Box className={bem(cn, { e: "BoxWrapper" })}>
      <Input
        aria-checked={checked ? "true" : "false"}
        checked={checked}
        className={bem(cn, { e: "Input" })}
        disabled={disabled}
        id={id}
        onChange={onChange}
        type="checkbox"
        onFocus={handleFocus}
        onBlur={handleBlur}
        onClick={label ? handleClick : undefined}
        tabIndex={tabIndex}
        {...rest}
      />
      <Box
        className={classnames(
          bem(cn, { e: "Box" }),
          bem(cn, { e: "Box", m: theme }),
          checked && bem(cn, { e: "Box", m: "checked" }),
        )}
      />
      {checked && (
        <Icon
          className={classnames(
            bem(cn, { e: "Check" }),
            bem(cn, { e: "Check", m: theme }),
          )}
          icon={ICON_TYPE.CHECK}
        />
      )}
    </Box>
  );

  const checkboxWithLabel =
    label || children ? (
      <Label
        className={bem(cn, { e: "Label" })}
        isRequired={calculatedRequired && !hideRequiredStyles}
      >
        {bareCheckbox}
        <Text
          as="span"
          className={classnames(
            bem(cn, { e: "LabelText" }),
            bem(cn, { e: "LabelText", m: theme }),
          )}
        >
          {label}
          {children}
        </Text>
      </Label>
    ) : (
      bareCheckbox
    );

  return (
    <Box
      className={classes}
      onClick={label ? undefined : handleClick}
      {...rest}
    >
      {checkboxWithLabel}
    </Box>
  );
};

Checkbox.Group = CheckboxGroup;
