'use client';

import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils';
import { AlertCircle } from 'lucide-react';
import * as React from 'react';

/*
 * Campo de formulario: etiqueta, control, ayuda y error, siempre en el mismo
 * orden y con la misma pinta.
 *
 * Conecta solo los atributos de accesibilidad (`htmlFor`, `aria-describedby`,
 * `aria-invalid`) para que quien usa lector de pantalla escuche el error junto
 * al campo, y para que el mensaje no dependa de que alguien se acuerde de
 * pintarlo de rojo.
 */

type FieldProps = {
  label: string;
  htmlFor: string;
  /** Texto de ayuda bajo el campo. Se oculta cuando hay error. */
  hint?: string;
  error?: string;
  required?: boolean;
  /**
   * Poné `nested` cuando el control venga envuelto en otro elemento — por
   * ejemplo un input con un botón de "ver contraseña" al lado. En ese caso
   * Field no toca nada y vos ponés `id`, `aria-invalid` y `aria-describedby`
   * sobre el input de verdad. Si no, el `id` termina en el div que envuelve y
   * la etiqueta deja de enfocar el campo al tocarla.
   */
  nested?: boolean;
  className?: string;
  children: React.ReactNode;
};

export function Field({
  label,
  htmlFor,
  hint,
  error,
  required,
  nested = false,
  className,
  children,
}: FieldProps) {
  const hintId = `${htmlFor}-hint`;
  const errorId = `${htmlFor}-error`;

  return (
    <div className={cn('space-y-1.5', className)}>
      <Label htmlFor={htmlFor} className="flex items-center gap-1">
        {label}
        {required ? (
          <span className="text-destructive" aria-hidden>
            *
          </span>
        ) : null}
        {required ? <span className="sr-only">(obligatorio)</span> : null}
      </Label>

      {/*
       * El control se clona para inyectarle los atributos de accesibilidad sin
       * que cada pantalla tenga que repetirlos.
       *
       * Si el hijo ya trae `id` propio no se le toca nada: eso pasa cuando el
       * control viene envuelto (por ejemplo un input con un botón de "ver
       * contraseña" al lado). En ese caso el `id` tiene que ir sobre el input
       * de verdad, no sobre el div que lo envuelve, o la etiqueta termina
       * apuntando a un contenedor y deja de enfocar el campo al tocarla.
       */}
      {!nested && React.isValidElement(children) && !(children.props as { id?: string }).id
        ? React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
            id: htmlFor,
            'aria-invalid': error ? true : undefined,
            'aria-describedby': error ? errorId : hint ? hintId : undefined,
            className: cn(
              (children.props as { className?: string }).className,
              error && 'border-destructive focus-visible:border-destructive focus-visible:ring-destructive/25',
            ),
          })
        : children}

      {error ? (
        <p id={errorId} role="alert" className="flex items-start gap-1.5 text-sm font-medium text-destructive">
          <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
          {error}
        </p>
      ) : hint ? (
        <p id={hintId} className="text-sm text-muted-foreground">
          {hint}
        </p>
      ) : null}
    </div>
  );
}
