import { type ReactNode, useId } from "react";
import {
  type FieldValues,
  type Path,
  type RegisterOptions,
  useFormContext,
  useWatch,
} from "react-hook-form";

import { charCountClass } from "../../utils/charCount.ts";

interface TextFieldProps<T extends FieldValues> {
  name: Path<T>;
  label?: string;
  /** Optional control rendered to the right of the label (e.g. a reset button). */
  labelAction?: ReactNode;
  type?: "text" | "email" | "password" | "url" | "search";
  placeholder?: string;
  autoComplete?: string;
  autoFocus?: boolean;
  disabled?: boolean;
  rules?: RegisterOptions<T, Path<T>>;
  /** Cap input length at `max` (native `maxLength`). */
  maxLength?: number;
  /** Show a live `n / maxLength` counter. Requires `maxLength`. */
  showCount?: boolean;
}

/** Live `n / max` counter; isolated so only counted fields subscribe to value. */
function CharCounter<T extends FieldValues>(
  { name, max }: { name: Path<T>; max: number },
) {
  const value = useWatch({ name }) as string | undefined;
  const len = value?.length ?? 0;
  return (
    <span className={`text-editor-count${charCountClass(len, max)}`}>
      {len} / {max}
    </span>
  );
}

/**
 * Native text input wired to react-hook-form via the surrounding `FormProvider`.
 * Renders a label, the input (with an optional character counter), and a
 * validation hint sourced from `formState.errors[name]`.
 */
export function TextField<T extends FieldValues>({
  name,
  label,
  labelAction,
  type = "text",
  placeholder,
  autoComplete,
  autoFocus,
  disabled,
  rules,
  maxLength,
  showCount = false,
}: TextFieldProps<T>) {
  const id = useId();
  const { register, formState: { errors } } = useFormContext<T>();
  const error = errors[name];
  const counted = showCount && maxLength != null;

  return (
    <div className="form-field">
      {(label || labelAction) && (
        <div className="form-label-row">
          {label
            ? (
              <label className="form-label" htmlFor={id}>
                {label}
              </label>
            )
            : <span />}
          {labelAction}
        </div>
      )}
      <div className={counted ? "input-with-count" : undefined}>
        <input
          id={id}
          type={type}
          placeholder={placeholder}
          autoComplete={autoComplete}
          autoFocus={autoFocus}
          disabled={disabled}
          maxLength={maxLength}
          aria-invalid={error ? true : undefined}
          {...register(name, rules)}
        />
        {counted && <CharCounter<T> name={name} max={maxLength} />}
      </div>
      {error?.message && (
        <span className="form-hint form-hint--error">
          {String(error.message)}
        </span>
      )}
    </div>
  );
}
