"use client";

import { useState } from "react";
import type {
  ChangeEventHandler,
  InputHTMLAttributes,
  Ref,
} from "react";
import { cn } from "@/lib/cn";

type FloatInputProps = InputHTMLAttributes<HTMLInputElement> & {
  label: string;
  error?: string;
  ref?: Ref<HTMLInputElement>;
  /** Live-filter keystrokes: "name" allows letters, spaces, dots, hyphens, apostrophes only */
  filter?: "name";
};

/* Floating-label input: the label rests inside the field and docks into
   the top border on focus / when filled. CSS-only via peer. */
const NAME_OK = /[A-Za-z .'-]/;

export default function FloatInput({
  label,
  error,
  required,
  id,
  className,
  ref,
  filter,
  onChange,
  defaultValue,
  value: valueProp,
  ...props
}: FloatInputProps) {
  // Controlled internally so live-filtering never fights React's value
  // tracker (direct DOM mutation gets wiped on the next render).
  const [inner, setInner] = useState(() =>
    valueProp !== undefined
      ? String(valueProp)
      : defaultValue !== undefined
        ? String(defaultValue)
        : ""
  );
  const value = valueProp !== undefined ? String(valueProp) : inner;
  const userChange = onChange as
    | ChangeEventHandler<HTMLInputElement>
    | undefined;
  // Single source of truth: sanitize through state on every change.
  // (Blocking inside beforeinput/paste handlers is all-or-nothing and
  // would reject entire fills, pastes and autofills over one bad char.)
  const guardChange: ChangeEventHandler<HTMLInputElement> = (e) => {
    // Filtered mode sanitizes through state (never direct DOM mutation).
    if (filter === "name") {
      setInner([...e.target.value].filter((ch) => NAME_OK.test(ch)).join(""));
    } else if (valueProp === undefined) {
      setInner(e.target.value);
    }
    userChange?.(e);
  };
  return (
    <div className={cn("min-w-0", className)}>
      <div className="relative">
        <input
          id={id}
          ref={ref}
          required={required}
          placeholder=" "
          value={value}
          onChange={guardChange}
          {...props}
          className={cn(
            "peer block h-12 w-full rounded-xl border bg-white px-3.5 pt-2 text-sm font-normal text-ink placeholder-transparent focus:outline-none focus-visible:outline-none",
            error
              ? "border-red-400 focus:border-red-500"
              : "border-[#d5dce8] focus:border-brand"
          )}
        />
        <label
          htmlFor={id}
          className={cn(
            "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 bg-white px-1 text-[15px] transition-all",
            "peer-focus:top-0 peer-focus:text-xs peer-focus:font-semibold",
            "peer-[:not(:placeholder-shown)]:top-0 peer-[:not(:placeholder-shown)]:text-xs peer-[:not(:placeholder-shown)]:font-semibold",
            error
              ? "text-red-500"
              : "text-muted peer-focus:text-brand peer-[:not(:placeholder-shown)]:text-brand"
          )}
        >
          {label}
          {required && (
            <span aria-hidden="true" className="ml-0.5 text-red-500">*</span>
          )}
        </label>
      </div>
      {error && (
        <span className="mt-1 block text-xs font-normal text-red-600">
          {error}
        </span>
      )}
    </div>
  );
}
