import { clsx } from '@trail-ui/shared-utils';
import type { RadioSlots, RadioVariantProps, SlotsToClasses } from '@trail-ui/theme';
import { radio } from '@trail-ui/theme';
import {
  ForwardedRef,
  ReactNode,
  cloneElement,
  forwardRef,
  isValidElement,
  useContext,
  useMemo,
} from 'react';
import type { RadioProps as AriaRadioProps } from 'react-aria-components';
import { Radio as AriaRadio } from 'react-aria-components';
import { RadioGroupThemeContext } from './radio-group';

export interface RadioIconProps {
  'data-checked'?: string;
  isSelected?: boolean;
  className?: string;
}

export interface RadioProps extends AriaRadioProps, RadioVariantProps {
  /**
   * The content to be displayed as the radio control.
   */
  control?: ReactNode | ((props: RadioIconProps) => ReactNode);
  /**
   * Whether the radio control should be displayed.
   */
  isDisplayControl?: boolean;
  /**
   * Classes object to style the radio and its children.
   */
  classNames?: SlotsToClasses<RadioSlots>;
  className?: string;
}

function Radio(props: RadioProps, ref: ForwardedRef<HTMLLabelElement>) {
  const groupThemeContext = useContext(RadioGroupThemeContext);

  const {
    size = groupThemeContext?.size ?? 'md',
    color = groupThemeContext?.color ?? 'purple',
    control,
    isDisplayControl = true,
    className,
    classNames,
    children,
    ...otherProps
  } = props;

  const slots = useMemo(
    () =>
      radio({
        color,
        size,
      }),
    [color, size],
  );

  const baseStyles = clsx(classNames?.base, className);

  return (
    <AriaRadio ref={ref} className={slots.base({ class: baseStyles })} {...otherProps}>
      {(renderProps) => {
        const iconProps = {
          'data-checked': String(renderProps.isSelected),
          isSelected: renderProps.isSelected,
        };

        let clonedControl;
        if (typeof control === 'function') {
          clonedControl = control(iconProps);
        } else if (isValidElement(control)) {
          clonedControl = cloneElement(control, iconProps);
        } else {
          const controlClass =
            renderProps.isHovered && !renderProps.isSelected
              ? clsx(classNames?.control, 'bg-neutral-100')
              : classNames?.control;

          clonedControl = (
            <span aria-hidden="true" className={slots.control({ class: controlClass })}>
              <span className={slots.point({ class: classNames?.point })} />
            </span>
          );
        }

        const displayControl = isDisplayControl && clonedControl;

        return (
          <>
            {displayControl}
            {children && (
              <span className={slots.label({ class: classNames?.label })}>
                {typeof children === 'function' ? children(renderProps) : children}
              </span>
            )}
          </>
        );
      }}
    </AriaRadio>
  );
}

/**
 * A checkbox allows a user to select multiple items from a list of individual items, or
 * to mark one individual item as selected.
 */
const _Radio = forwardRef(Radio);

export { _Radio as Radio };
