"use client"

import { forwardRef, type HTMLAttributes, type WheelEvent } from "react"
import { Text, TextField } from "react-aria-components"
import { Controller, useFormContext } from "react-hook-form"
import { useIntl, type IntlShape } from "react-intl"
import { cx } from "class-variance-authority"

import { Error } from "../ErrorMessage/Error"
import { mergeRefs } from "../utils/mergeRefs"
import { MaterialIcon, MaterialIconProps } from "../../Icons/MaterialIcon"
import { Input } from "../../Input"

import styles from "./input.module.css"

import type { FormInputProps } from "./input"

const defaultErrorFormatter = (
  _intl: IntlShape,
  errorMessage?: string
): string => errorMessage ?? ""

export const FormInput = forwardRef<HTMLInputElement, FormInputProps>(
  function FormInput(
    {
      autoComplete,
      className = "",
      description = "",
      descriptionIcon = "info" as MaterialIconProps["icon"],
      disabled = false,
      errorFormatter,
      hideError,
      inputMode,
      label,
      labelPosition = "floating",
      maxLength,
      name,
      id,
      placeholder,
      readOnly = false,
      registerOptions = {},
      type = "text",
      validationState,
      ...props
    },
    ref
  ) {
    const intl = useIntl()
    const { control } = useFormContext()

    const formatErrorMessage = errorFormatter ?? defaultErrorFormatter

    // Number input: prevent scroll from changing value
    const numberAttributes: HTMLAttributes<HTMLInputElement> =
      type === "number"
        ? {
            onWheel: (evt: WheelEvent<HTMLInputElement>) => {
              evt.currentTarget.blur()
            },
          }
        : {}

    return (
      <Controller
        control={control}
        name={name}
        rules={registerOptions}
        render={({ field, fieldState }) => {
          const isDisabled = disabled || field.disabled
          const hasError = fieldState.invalid && !hideError
          const showDescription = description && !fieldState.error

          return (
            // Note: No aria-label needed on TextField since the Input component
            // always renders a visible label that provides the accessible name
            <TextField
              className={cx(styles.wrapper, className)}
              isDisabled={isDisabled}
              isReadOnly={readOnly}
              isInvalid={fieldState.invalid}
            >
              <Input
                {...props}
                {...numberAttributes}
                ref={mergeRefs(field.ref, ref)}
                name={field.name}
                onBlur={field.onBlur}
                onChange={field.onChange}
                value={field.value ?? ""}
                autoComplete={autoComplete}
                id={id ?? field.name}
                label={label}
                labelPosition={labelPosition}
                maxLength={maxLength}
                placeholder={placeholder}
                readOnly={readOnly}
                disabled={isDisabled}
                required={!!registerOptions.required}
                type={type}
                inputMode={inputMode}
                // Only 'warning' is passed through; error state is handled via isInvalid
                data-validation-state={validationState}
              />
              {showDescription ? (
                <Text className={styles.description} slot="description">
                  <MaterialIcon icon={descriptionIcon} size={20} />
                  {description}
                </Text>
              ) : null}
              {hasError && fieldState.error ? (
                <Text slot="errorMessage" aria-live="polite">
                  <Error>
                    {formatErrorMessage(intl, fieldState.error.message)}
                  </Error>
                </Text>
              ) : null}
            </TextField>
          )
        }}
      />
    )
  }
)

FormInput.displayName = "FormInput"
