// @design-system: primitives/FormField
// Registered at /design-system#formfield-primitive - Phase 3 (Agent 3E) will render the gallery.

import { Children, isValidElement, cloneElement, useId } from 'react';
import type { ReactNode, ReactElement } from 'react';
import { cn } from '@/lib/cn';
import { Label } from '../Label';
import { LabelWithTooltip } from '../LabelWithTooltip';

/** Props for the FormField component */
export interface FormFieldProps {
  /** The label text. */
  label: string;
  /** The form control to render inside. */
  children: ReactNode;
  /** Validation error message. Shown in red below the control. */
  error?: string;
  /** Hint message. Shown in muted color below the control. */
  hint?: string;
  /** Marks the field as required (asterisk on label + aria-required). */
  required?: boolean;
  /** The id of the form control. If omitted, an id is generated. */
  htmlFor?: string;
  /** Optional help tooltip shown next to the label as an info-icon button. */
  tooltip?: string;
  /** Additional classes on the wrapper div. */
  className?: string;
}

/**
 * Multideal FormField - composes Label + control + error + hint.
 *
 * Sets up `aria-describedby` on the hint/error elements so screen readers
 * announce them automatically. Pass `htmlFor` that matches your input's `id`.
 *
 * @example
 * ```tsx
 * <FormField label={t('phone')} required error={errors.phone?.message} htmlFor="phone">
 *   <Input id="phone" type="tel" />
 * </FormField>
 * ```
 */
export function FormField({
  label,
  children,
  error,
  hint,
  required,
  htmlFor,
  tooltip,
  className,
}: FormFieldProps) {
  const uid = useId();
  const fieldId = htmlFor ?? uid;
  const hintId = hint ? `${fieldId}-hint` : undefined;
  const errorId = error ? `${fieldId}-error` : undefined;

  // Inject aria-describedby into the child element
  const describedBy = [hintId, errorId].filter(Boolean).join(' ') || undefined;

  const controlAria = {
    ...(describedBy ? { 'aria-describedby': describedBy } : {}),
    ...(error ? { 'aria-invalid': true } : {}),
    ...(required ? { 'aria-required': true } : {}),
  };

  // ARIA state belongs on the labelled control, which may sit inside a composite wrapper.
  const injectAria = (node: ReactNode, depth: number): { node: ReactNode; injected: boolean } => {
    if (!isValidElement(node)) return { node, injected: false };
    const element = node as ReactElement<Record<string, unknown> & { children?: ReactNode }>;
    if (element.props.id === fieldId) {
      return { node: cloneElement(element, controlAria), injected: true };
    }
    if (depth === 0 || element.props.children == null) return { node, injected: false };
    let injected = false;
    const nested = Children.map(element.props.children, (child) => {
      if (injected) return child;
      const result = injectAria(child, depth - 1);
      injected ||= result.injected;
      return result.node;
    });
    return injected ? { node: cloneElement(element, {}, nested), injected } : { node, injected };
  };

  const resolved = injectAria(children, 2);
  const childWithAria = resolved.injected
    ? resolved.node
    : isValidElement(children)
      ? cloneElement(children as ReactElement<Record<string, unknown>>, controlAria)
      : children;

  return (
    <div className={cn('flex flex-col gap-1', className)}>
      {tooltip ? (
        <LabelWithTooltip label={label} tooltip={tooltip} htmlFor={fieldId} required={required} />
      ) : (
        <Label htmlFor={fieldId} required={required}>
          {label}
        </Label>
      )}

      {childWithAria}

      {hint && !error && (
        <p id={hintId} className="text-xs text-neutral-600">
          {hint}
        </p>
      )}

      {error && (
        <p id={errorId} role="alert" className="text-danger-600 text-xs">
          {error}
        </p>
      )}
    </div>
  );
}
