import * as React from 'react';

import type { ToggleButtonProps } from '../toggle-button';
import { styled } from '@mui/material/styles';

import type { Theme } from '../../@styles/theme-provider';

const ToggleButtonGroupRoot = styled('div', {
  name: 'ToggleButtonGroup',
  slot: 'Root'
})(({ theme }: { theme: Theme }) => ({
  display: 'inline-flex',
  '& > *:not(:first-of-type)': {
    marginLeft: theme.spacing(2)
  }
}));

/**
 * Determine if the toggle button value matches, or is contained in, the candidate group value.
 * @param value
 * @param candidate
 * @returns
 */
function isValueSelected(
  value: ToggleButtonProps['value'],
  candidate: ToggleButtonGroupProps['value']
) {
  if (typeof candidate === 'undefined' || typeof value === 'undefined') {
    return false;
  }

  if (Array.isArray(candidate)) {
    return candidate.indexOf(value) >= 0;
  }

  return value === candidate;
}

export interface ToggleButtonGroupProps {
  /**
   * The content of the component.
   */
  children: ToggleButtonProps['children'];
  /**
   * If `true`, the component is disabled. This implies that all ToggleButton children will be disabled.
   * @default false
   */
  disabled?: boolean;
  /**
   * If `true`, allows multiple child ToggleButton values to be selected (checkbox behavior).
   * @default false
   */
  multiple?: boolean;
  /**
   * The callback is fired when the value changes.
   * @param {React.MouseEvent<HTMLElement>} event The event source of the callback.
   * @param value Value of the selected button.
   */
  onChange?: (event: React.MouseEvent<HTMLElement>, value: string | string[] | null) => void;
  /**
   * The currently selected value within the group or an array of selected
   * values when `multiple` is true (the value must have reference equality
   * with the option in order to be selected).
   */
  value?: string | string[] | null;
  /**
   * Custom class name in case you need to add custom styles to the component.
   */
  className?: string;
}

/**
 * Exclusive Selection vs. Multiple Selection
 * ------------------------------------------
 * By default, ButtonToggleGroup acts like a radio-button group- only one
 * button can be selected. In this mode, the value of the ButtonToggleGroup
 * will reflect the value of the selected button.
 */
const ToggleButtonGroup = React.forwardRef(function (
  props: ToggleButtonGroupProps,
  ref: React.Ref<HTMLDivElement>
) {
  const {
    children,
    disabled = false,
    multiple = false,
    value: _value,
    onChange,
    ...otherProps
  } = props;

  let value: ToggleButtonGroupProps['value'];
  if (!multiple) {
    value = _value || null;
  } else if (typeof _value === 'string') {
    value = [_value];
  } else {
    value = _value || [];
  }

  const handleExclusiveChange = (
    event: React.MouseEvent<HTMLElement>,
    buttonValue: ToggleButtonProps['value']
  ) => {
    onChange?.(event, value === buttonValue ? null : buttonValue);
  };

  const handleMultipleChange = (
    event: React.MouseEvent<HTMLElement>,
    buttonValue: NonNullable<ToggleButtonProps['value']>
  ) => {
    if (!onChange) return;

    const index = value ? value.indexOf(buttonValue) : -1;
    let newValue: string[];

    if (value && index >= 0) {
      newValue = (value as string[]).slice();
      newValue.splice(index, 1);
    } else {
      newValue = value ? (value as string[]).concat(buttonValue) : [buttonValue];
    }

    onChange(event, newValue);
  };

  return (
    <ToggleButtonGroupRoot ref={ref} role="group" data-testid="toggleButtonGroup" {...otherProps}>
      {React.Children.map(children, child => {
        if (!React.isValidElement(child)) {
          return null;
        }

        return React.cloneElement(child, {
          disabled: child.props.disabled || disabled,
          selected:
            child.props.selected === undefined
              ? isValueSelected(child.props.value, value)
              : child.props.selected,
          onChange: multiple ? handleMultipleChange : handleExclusiveChange,
          ...child.props
        });
      })}
    </ToggleButtonGroupRoot>
  );
});

const m = React.memo(ToggleButtonGroup);
export { m as ToggleButtonGroup };
