import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
  Input,
} from "@repo/shadcn-ui/components";

import { cn } from "@repo/shadcn-ui/lib/utils";
import { CheckIcon, Loader2Icon, XIcon } from "lucide-react";
import { Control, FieldPath, FieldValues, useController, useFormState } from "react-hook-form";
import { useTranslation } from "../../locales/i18n.ts";

/**
 *
 * // Email con normalización y contador
 * <TextField
 *   name="email"
 *   label="Email"
 *   description="Usa tu email de trabajo"
 *   typePreset="email"
 *   placeholder="tú@empresa.com"
 *   icon={<Mail className="h-4 w-4" />}
 *   iconPosition="left"
 *   maxLength={120}
 *   showCounter
 *   clearable
 * />
 *
 *   // Teléfono con normalización (mantiene + y dígitos), prefix clicable
 * <TextField
 *   name="mobile"
 *   label="Móvil"
 *   description="Incluye prefijo internacional"
 *   typePreset="phone"
 *   placeholder="+34 600 000 000"
 *   prefix={<span className="text-xs">+34</span>}
 *   onPrefixClick={() => {
 * // Alternar prefijo, o abrir selector de país...
 * }}
 * clearable
 * />
 *
 * // Número decimal con normalización (',' → '.'), suffix clicable para unidad
 * <TextField
 *   name="price"
 *   label="Precio"
 *   description="Con IVA"
 *   typePreset="number"
 *   placeholder="0.00"
 *   suffix={<span className="text-xs">EUR</span>}
 *   onSuffixClick={() => {
 * // Cambiar moneda, etc.
 * }}
 * inputMode="decimal" // si quieres sobreescribir
 * icon={<Hash className="h-4 w-4" />}
 * iconPosition="left"
 * />
 *
 * // Password (sin normalizaciones)
 * <TextField
 *   name="password"
 *   label="Contraseña"
 *   typePreset="password"
 *   placeholder="••••••••"
 *   autoComplete="new-password"
 * />
 *
 */

/** Presets de comportamiento */
type TextFieldTypePreset = "text" | "email" | "phone" | "number" | "password";

type CommonInputProps = Omit<
  React.InputHTMLAttributes<HTMLInputElement>,
  "name" | "value" | "onChange" | "onBlur" | "ref" | "type"
>;

type Normalizer = (value: string) => string;

type TextFieldProps<TFormValues extends FieldValues> = CommonInputProps & {
  control: Control<TFormValues>;
  name: FieldPath<TFormValues>;
  label?: string;
  placeholder?: string;
  description?: string;
  required?: boolean;
  readOnly?: boolean;
  className?: string;
  inputClassName?: string;

  typePreset?: TextFieldTypePreset;

  icon?: React.ReactNode; // Icono con tamaño: <MailIcon className="h-[18px] w-[18px]" />
  iconPosition?: "left" | "right"; // 'left' por defecto

  /** Addons laterales (pueden ser clicables, a diferencia de `icon`) */
  prefix?: React.ReactNode;
  suffix?: React.ReactNode;
  onPrefixClick?: () => void;
  onSuffixClick?: () => void;

  /** UX extra */
  clearable?: boolean;
  submitOnEnter?: boolean;
  disabledWhileSubmitting?: boolean;
  showSuccessWhenValid?: boolean;
  showValidatingSpinner?: boolean;

  /** Transformaciones */
  transformOnBlur?: (value: string) => string;
  normalizeOnChange?: (value: string) => string;

  /** Contador de caracteres (si usas maxLength) */
  showCounter?: boolean;

  /** Forzar type/inputMode/autocomplete si no quieres los del preset */
  forceType?: React.HTMLInputTypeAttribute;
  forceInputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
  forceAutoComplete?: string;
};

/* ---------- Helpers de presets ---------- */

function presetInputType(p: TextFieldTypePreset): React.HTMLInputTypeAttribute {
  switch (p) {
    case "password":
      return "password";
    case "number":
      return "text"; // usamos text + normalización para control fino (decimales, signos)
    case "email":
      return "email";
    case "phone":
      return "tel";
    case "text":
      return "text";
    default:
      return "text";
  }
}

function presetInputMode(
  p: TextFieldTypePreset
): React.HTMLAttributes<HTMLInputElement>["inputMode"] {
  switch (p) {
    case "phone":
      return "tel";
    case "number":
      return "decimal";
    case "email":
      return "email";
    default:
      return undefined;
  }
}

function presetAutoComplete(p: TextFieldTypePreset): string | undefined {
  switch (p) {
    case "email":
      return "email";
    case "phone":
      return "tel";
    case "password":
      return "current-password";
    default:
      return undefined;
  }
}

function presetTransformOnBlur(p: TextFieldTypePreset): ((v: string) => string) | undefined {
  switch (p) {
    case "email":
      return (v) => v.trim().toLowerCase();
    case "text":
      return undefined;
    case "password":
      return undefined;
    case "phone":
      return undefined;
    case "number":
      return undefined;
    default:
      return undefined;
  }
}

/** Normalizador “suave” para números: permite signo inicial y un solo separador decimal '.' */
function normalizeNumber(value: string): string {
  // Sustituye comas por punto y elimina caracteres inválidos
  let v = value.replace(/,/g, ".");
  // Mantén solo dígitos, un punto y signo inicial
  v = v
    .replace(/[^\d.+-]/g, "")
    .replace(/(?!^)-/g, "") // solo un signo al inicio
    .replace(/(\..*)\./g, "$1"); // solo un punto
  return v;
}

/** Normalizador para teléfonos: mantiene dígitos y '+' inicial */
function normalizePhone(value: string): string {
  let v = value.replace(/[^\d+]/g, "");
  v = v.replace(/(?!^)\+/g, ""); // '+' solo al inicio
  return v;
}

function presetNormalizeOnChange(p: TextFieldTypePreset): Normalizer | undefined {
  switch (p) {
    case "phone":
      return normalizePhone;
    case "number":
      return normalizeNumber;
    default:
      return undefined;
  }
}

/* ---------- Componente ---------- */

export function TextField<TFormValues extends FieldValues>({
  control,
  name,
  label,
  description,
  required,
  readOnly,
  className,
  inputClassName,
  typePreset = "text",

  icon,
  iconPosition = "left",

  prefix,
  suffix,
  onPrefixClick,
  onSuffixClick,

  clearable = false,
  submitOnEnter = false,
  disabledWhileSubmitting = true,
  showSuccessWhenValid = false,
  showValidatingSpinner = true,

  transformOnBlur,
  normalizeOnChange,

  showCounter = false,

  forceType,
  forceInputMode,
  forceAutoComplete,

  maxLength,
  ...rest
}: TextFieldProps<TFormValues>) {
  const { t } = useTranslation();

  const { isSubmitting, isValidating } = useFormState({ control, name });
  const { field, fieldState } = useController({ control, name });

  // Presets → defaults (permiten override por props explícitas)
  const effectiveType = forceType ?? presetInputType(typePreset);
  const effectiveInputMode = forceInputMode ?? presetInputMode(typePreset);
  const effectiveAutoComplete = forceAutoComplete ?? presetAutoComplete(typePreset);
  const effectiveTransformOnBlur = transformOnBlur ?? presetTransformOnBlur(typePreset);
  const effectiveNormalizeOnChange = normalizeOnChange ?? presetNormalizeOnChange(typePreset);

  const hasIcon = Boolean(icon);
  const isLeftIcon = iconPosition === "left";
  const hasPrefix = prefix != null;
  const hasSuffix = suffix != null;

  // padding a partir de adornos
  const inputPadding = cn(
    hasIcon && isLeftIcon && "pl-10",
    hasIcon && !isLeftIcon && "pr-10",
    hasPrefix && "pl-10",
    hasSuffix && "pr-10"
  );

  const invalid = fieldState.invalid && (fieldState.isTouched || fieldState.isDirty);
  const valid =
    !fieldState.invalid &&
    (fieldState.isTouched || fieldState.isDirty) &&
    field.value != null &&
    String(field.value).length > 0;

  const disabled = (disabledWhileSubmitting && isSubmitting) || rest.disabled || readOnly;

  const describedById = description ? `${name}-desc` : undefined;
  const errorId = fieldState.error ? `${name}-err` : undefined;

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const raw = e.target.value;
    const next = effectiveNormalizeOnChange ? effectiveNormalizeOnChange(raw) : raw;
    field.onChange(next);
  }

  function handleBlur(e: React.FocusEvent<HTMLInputElement>) {
    if (effectiveTransformOnBlur) {
      const next = effectiveTransformOnBlur(e.target.value ?? "");
      if (next !== e.target.value) {
        field.onChange(next);
      }
    }
    field.onBlur();
  }

  function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
    if (submitOnEnter && e.key === "Enter") {
      const form = (e.currentTarget as HTMLInputElement).form;
      if (form) form.requestSubmit();
    }
  }

  function handleClear() {
    field.onChange("");
  }

  const valueLength = (field.value?.length ?? 0) as number;

  return (
    <FormField
      control={control}
      name={name}
      render={({ field }) => (
        <FormItem className={cn("space-y-0", className)}>
          {label && (
            <div className='mb-1 flex justify-between gap-2'>
              <div className='flex items-center gap-2'>
                <FormLabel htmlFor={name} className='m-0'>
                  {label}
                </FormLabel>
                {required && (
                  <span className='text-xs text-destructive'>{t("common.required")}</span>
                )}
              </div>
              {/* Punto “unsaved” */}
              {fieldState.isDirty && (
                <span className='text-[10px] text-muted-foreground'>{t("common.modified")}</span>
              )}
            </div>
          )}
          <FormControl>
            <div className={cn("relative")}>
              {/* Prefix clicable (si tiene onClick) */}
              {hasPrefix && (
                <button
                  type={onPrefixClick ? "button" : undefined}
                  onClick={onPrefixClick}
                  tabIndex={onPrefixClick ? 0 : -1}
                  className={cn(
                    "absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground",
                    !onPrefixClick && "pointer-events-none"
                  )}
                  aria-label='prefix'
                >
                  {prefix}
                </button>
              )}

              {/* Icono decorativo */}
              {hasIcon && (
                <span
                  aria-hidden='true'
                  className={cn(
                    "pointer-events-none absolute top-1/2 -translate-y-1/2",
                    isLeftIcon ? "left-3" : "right-3"
                  )}
                >
                  {icon}
                </span>
              )}

              <Input
                id={name}
                type={effectiveType}
                inputMode={effectiveInputMode}
                autoComplete={effectiveAutoComplete}
                placeholder={rest.placeholder}
                value={field.value ?? ""}
                onChange={handleChange}
                onBlur={handleBlur}
                onKeyDown={handleKeyDown}
                ref={field.ref}
                disabled={disabled}
                readOnly={readOnly}
                aria-invalid={invalid || undefined}
                aria-describedby={cn(describedById, errorId)}
                aria-errormessage={errorId}
                aria-busy={(showValidatingSpinner && isValidating) || undefined}
                maxLength={maxLength}
                {...rest}
                className={cn(
                  "placeholder:font-normal placeholder:italic bg-background",
                  inputPadding,
                  invalid && "border-destructive focus-visible:ring-destructive",
                  valid && showSuccessWhenValid && "border-green-500 focus-visible:ring-green-500",
                  // Si hay suffix interactivo y spinner/check, reserva padding derecho
                  (hasSuffix || showValidatingSpinner || valid) && "pr-10",
                  inputClassName
                )}
              />

              {/* Suffix clicable */}
              {hasSuffix && (
                <button
                  type={onSuffixClick ? "button" : undefined}
                  onClick={onSuffixClick}
                  tabIndex={onSuffixClick ? 0 : -1}
                  className={cn(
                    "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground",
                    !onSuffixClick && "pointer-events-none"
                  )}
                  aria-label='suffix'
                >
                  {suffix}
                </button>
              )}

              {/* Spinner de validación */}
              {showValidatingSpinner && isValidating && (
                <span className='absolute right-2 top-1/2 -translate-y-1/2'>
                  <Loader2Icon className='h-4 w-4 animate-spin' />
                </span>
              )}

              {/* Check de válido */}
              {showSuccessWhenValid && valid && !isValidating && !invalid && (
                <span className='absolute right-2 top-1/2 -translate-y-1/2 text-green-600'>
                  <CheckIcon className='h-4 w-4' />
                </span>
              )}

              {/* Botón clear */}
              {clearable && !disabled && (field.value ?? "") !== "" && (
                <button
                  type='button'
                  aria-label='Borrar'
                  onClick={handleClear}
                  className='absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 hover:bg-muted'
                >
                  <XIcon className='h-4 w-4' />
                </button>
              )}
            </div>
          </FormControl>

          <div className='mt-1 flex items-start justify-between'>
            <FormDescription
              id={describedById}
              className={cn("text-xs truncate", !description && "invisible")}
            >
              {description || "\u00A0"}
            </FormDescription>

            {showCounter && typeof maxLength === "number" && (
              <p className='text-xs text-muted-foreground'>
                {valueLength} / {maxLength}
              </p>
            )}
          </div>

          <FormMessage id={errorId} />
        </FormItem>
      )}
    />
  );
}
