import { forwardRef } from 'react';
import type { InputHTMLAttributes, ReactNode } from 'react';
import { cx } from '../../utils/cx';
import { useFallbackId } from '../../utils/useId';
import { Field } from '../Field/Field';
import type { FieldOwnProps } from '../Field/Field';
import '../../styles/choice.css';
import './Radio.css';

export interface RadioProps
  extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
  label?: ReactNode;
  description?: ReactNode;
  invalid?: boolean;
}

/** One option in a radio group. Give every radio in a group the same `name`. */
export const Radio = forwardRef<HTMLInputElement, RadioProps>(function Radio(
  { label, description, invalid = false, id, className, ...rest },
  ref,
) {
  const inputId = useFallbackId(id);
  const descriptionId = `${inputId}-description`;

  return (
    <div className={cx('ds-choice', 'ds-radio', invalid && 'ds-choice--invalid', className)}>
      <span className="ds-choice__control">
        <input
          {...rest}
          ref={ref}
          id={inputId}
          type="radio"
          className="ds-choice__input"
          aria-invalid={invalid || undefined}
          aria-describedby={description != null ? descriptionId : undefined}
        />
        <span className="ds-choice__box ds-radio__box" aria-hidden="true">
          <span className="ds-radio__dot" />
        </span>
      </span>
      {(label != null || description != null) && (
        <span className="ds-choice__text">
          {label != null && (
            <label className="ds-choice__label" htmlFor={inputId}>
              {label}
            </label>
          )}
          {description != null && (
            <span id={descriptionId} className="ds-choice__description">
              {description}
            </span>
          )}
        </span>
      )}
    </div>
  );
});

export interface RadioGroupProps extends FieldOwnProps {
  /** Shared `name` for every radio inside. */
  name: string;
  /** Lays the options out in a row instead of a column. */
  direction?: 'vertical' | 'horizontal';
  children?: ReactNode;
  className?: string;
}

/**
 * Groups radios under a shared label, hint and error, and exposes them to
 * assistive tech as one control via `role="radiogroup"`.
 */
export function RadioGroup({
  name,
  direction = 'vertical',
  label,
  hint,
  error,
  required,
  fullWidth,
  children,
  className,
}: RadioGroupProps) {
  const groupId = useFallbackId();
  const messageId = `${groupId}-message`;

  return (
    <Field
      label={label}
      hint={hint}
      error={error}
      required={required}
      fullWidth={fullWidth}
      htmlFor={groupId}
      messageId={messageId}
      className={className}
    >
      <div
        id={groupId}
        role="radiogroup"
        aria-label={typeof label === 'string' ? label : undefined}
        aria-describedby={hint != null || error != null ? messageId : undefined}
        data-name={name}
        className={cx('ds-radio-group', `ds-radio-group--${direction}`)}
      >
        {children}
      </div>
    </Field>
  );
}
