import { ChangeEventHandler } from 'react';

import { useTestIdAttribute } from '../../hooks/useTestIdAttribute';
import { assertEmptyObject } from '../../utils/assertEmptyObject';
import { GeneralFormControlProps } from '../types';

import { SwitchSize } from './constants';
import { StyledSwitch } from './styled';

/** Props for {@link Switch} */
export interface SwitchProps extends GeneralFormControlProps<boolean> {
  /**
   * Switch control size
   *
   * @default {@link SwitchSize.Default}
   */
  size?: SwitchSize;
  tabIndex?: number;
}

/**
 * Switch input that provide custom `<input type="checkbox" />` design.
 *
 * Switch value can be at two states:
 *
 * | Value   | Example                                                  | State             |
 * | ------- | -------------------------------------------------------- | ----------------- |
 * | `false` | <Story id="form-switch--default" noCanvas />             | Unchecked         |
 * | `true`  | <Story id="form-switch--checked" noCanvas />             | Checked           |
 *
 * ```tsx
 * import { Switch } from "ui-kit";
 *
 * <Switch value={true} onChange={value => console.log(value)} />
 * ```
 *
 * Switch can be disabled by `disabled` prop.
 *
 * ```tsx
 * import { Switch } from "ui-kit";
 *
 * <Switch disabled />
 * ```
 *
 * Switch can be rendered in small mode by passing `size` prop:
 *
 * <Story id="form-switch--small" />
 *
 * ## Label support
 *
 * You can you you Switch with {@link FormField} to add `label` or description to Switch.
 *
 * Only label:
 *
 * <Story id="form-switch--with-label-and-description" />
 *
 * Label and description:
 *
 * <Story id="form-switch--with-label" />
 *
 * If you don't use {@link FormField} don't forget add `aria-label` to Switch to make it a11y valid.
 *
 * ```tsx
 * import { Switch } from "ui-kit";
 *
 * <Switch ariaLabel="Label" />
 * ```
 */
export function Switch(props: SwitchProps) {
  const {
    value,
    onChange,
    onBlur,
    ariaInvalid,
    ariaErrorMessage,
    ariaLabel,
    ariaDescribedBy,
    size,
    disabled,
    tabIndex,
    className,
    id,
    required,
    ariaRequired,
    testId,
    ...rest
  } = props;
  assertEmptyObject(rest);

  const testIdAttribute = useTestIdAttribute();

  const handleInputChange: ChangeEventHandler<HTMLInputElement> = (event) => {
    if (onChange && event.currentTarget.checked !== value) {
      onChange(event.currentTarget.checked);
    }
  };

  return (
    <StyledSwitch
      $size={size ?? SwitchSize.Default}
      aria-describedby={ariaDescribedBy}
      aria-errormessage={ariaErrorMessage}
      aria-invalid={ariaInvalid}
      aria-label={ariaLabel}
      aria-required={ariaRequired}
      checked={value}
      className={className}
      disabled={disabled}
      id={id}
      onBlur={onBlur}
      onChange={handleInputChange}
      required={required}
      role="switch"
      tabIndex={tabIndex}
      type="checkbox"
      {...{ [testIdAttribute]: testId }}
    />
  );
}
