"use client";

import * as React from "react";
import {
  Controller,
  type Control,
  type ControllerFieldState,
  type ControllerRenderProps,
  type FieldPath,
  type FieldValues,
} from "react-hook-form";

import { Label } from "@/components/ui/label";
import { Typography } from "@/components/ui/typography";
import { useTranslation } from "@/hooks/useTranslation";
import { cn } from "@/lib/utils";

interface FormFieldRenderArgs<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
> {
  /** RHF field bindings — spread onto the control (`{...field}`) */
  field: ControllerRenderProps<TFieldValues, TName>;
  fieldState: ControllerFieldState;
  /**
   * True once the field has an error AND was touched (or the form was
   * submitted) — pass to the control as `aria-invalid={hasError}`. Follows
   * the project convention of not flagging untouched fields.
   */
  hasError: boolean;
  /** id already linked to the rendered label — put it on the control */
  id: string;
}

interface FormFieldProps<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
> {
  control: Control<TFieldValues>;
  name: TName;
  /** Already-translated label text — pass `t("...")` */
  label?: React.ReactNode;
  /** Already-translated helper text shown under the control (hidden while an error shows) */
  description?: React.ReactNode;
  /**
   * Show the error state as the field changes, not only after blur/submit.
   * Use this when the UX should warn immediately while typing.
   */
  showErrorOnChange?: boolean;
  id?: string;
  className?: string;
  render: (
    args: FormFieldRenderArgs<TFieldValues, TName>,
  ) => React.ReactElement;
}

/**
 * Standard form field wrapper: Label + control + i18n error caption, bound to
 * react-hook-form via Controller. Zod schema messages are expected to be
 * dictionary keys (project convention, see `lib/validations/auth.schema.ts`) —
 * they are resolved through `t()` automatically.
 *
 * Usage:
 *   <FormField
 *     control={form.control}
 *     name="phoneNumber"
 *     label={t("team.form.phone")}
 *     render={({ field, hasError, id }) => (
 *       <PhoneInput id={id} aria-invalid={hasError} {...field} />
 *     )}
 *   />
 */
function FormField<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
>({
  control,
  name,
  label,
  description,
  showErrorOnChange = false,
  id,
  className,
  render,
}: FormFieldProps<TFieldValues, TName>) {
  const { t } = useTranslation();
  const fieldId = id ?? `field-${name}`;

  return (
    <Controller
      control={control}
      name={name}
      render={({ field, fieldState, formState }) => {
        const hasError =
          !!fieldState.error &&
          (fieldState.isTouched ||
            formState.submitCount > 0 ||
            (showErrorOnChange && fieldState.isDirty));

        return (
          <div
            data-slot="form-field"
            className={cn("flex flex-col gap-1.5", className)}
          >
            {label && <Label htmlFor={fieldId}>{label}</Label>}
            {render({ field, fieldState, hasError, id: fieldId })}
            {description && !hasError && (
              <Typography
                as="p"
                variant="caption"
                color="secondary"
                className="mt-0.5"
              >
                {description}
              </Typography>
            )}
            {hasError && (
              <Typography
                as="p"
                variant="caption"
                className="text-destructive mt-0.5"
              >
                {t(fieldState.error?.message ?? "")}
              </Typography>
            )}
          </div>
        );
      }}
    />
  );
}

export { FormField };
export type { FormFieldProps, FormFieldRenderArgs };
