import type { CSSProperties, ReactNode } from 'react';
import { cx } from '../../utils/cx';
import './Field.css';

/** Label/hint/error props shared by Input, Textarea and Select. */
export interface FieldOwnProps {
  /** Visible label. Omit only when a label exists elsewhere. */
  label?: ReactNode;
  /** Helper text under the control. Hidden while `error` is set. */
  hint?: ReactNode;
  /** Error message. Its presence also puts the control in its invalid state. */
  error?: ReactNode;
  /** Marks the field required and appends an asterisk to the label. */
  required?: boolean;
  /** Stretches the field to the width of its container. */
  fullWidth?: boolean;
}

export interface FieldProps extends FieldOwnProps {
  /** id of the control this field wraps — used for label association. */
  htmlFor: string;
  /** id applied to the hint/error text, for aria-describedby. */
  messageId: string;
  children: ReactNode;
  className?: string;
  style?: CSSProperties;
}

/**
 * Layout shell for a labelled form control. Exported so app code can wrap
 * custom controls in the same label/hint/error furniture the library uses.
 */
export function Field({
  label,
  hint,
  error,
  required,
  fullWidth,
  htmlFor,
  messageId,
  children,
  className,
  style,
}: FieldProps) {
  const message = error ?? hint;

  return (
    <div className={cx('ds-field', fullWidth && 'ds-field--full', className)} style={style}>
      {label != null && (
        <label className="ds-field__label" htmlFor={htmlFor}>
          {label}
          {required && (
            <span className="ds-field__required" aria-hidden="true">
              *
            </span>
          )}
        </label>
      )}
      {children}
      {message != null && (
        <p
          id={messageId}
          className={cx('ds-field__message', error != null && 'ds-field__message--error')}
        >
          {message}
        </p>
      )}
    </div>
  );
}
