import { cx } from 'class-variance-authority'
import {
  type ForwardedRef,
  forwardRef,
  useId,
  useImperativeHandle,
  useRef,
} from 'react'
import { Input as AriaInput, Label as AriaLabel } from 'react-aria-components'

import { InputLabel } from '../InputLabel'

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

import { MaterialIcon } from '../Icons/MaterialIcon'
import { Typography } from '../Typography'
import type { InputProps } from './types'
import { clearInput, useInputHasValue } from './utils'

const InputComponent = forwardRef(function AriaInputWithLabelComponent(
  {
    label,
    labelPosition = 'floating',
    leftIcon,
    rightIcon,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onRightIconClick,
    showClearContentIcon,
    placeholder,
    id,
    required,
    'data-validation-state': validationState,
    ...props
  }: InputProps & { 'data-validation-state'?: string },
  ref: ForwardedRef<HTMLInputElement>
) {
  // Create an internal ref that we can access
  const internalRef = useRef<HTMLInputElement>(null)

  // Generate a unique ID for the input
  // This is used to ensure the input is properly associated with the label
  // when the label is positioned above the input
  const generatedId = useId()

  // Forward the ref properly
  useImperativeHandle(ref, () => internalRef.current!, [])

  // Track whether input has a value (for showing/hiding clear button)
  const hasValue = useInputHasValue(props.value, internalRef)

  const onClearContent = () => {
    clearInput({
      inputRef: internalRef,
      onChange: props.onChange,
      value: props.value,
    })
  }
  // When labelPosition is 'top', restructure to have label outside container
  // We need an ID for proper label-input association
  if (labelPosition === 'top') {
    const inputId = id || generatedId

    return (
      <>
        <InputLabel
          required={required}
          className={cx(
            styles.labelAbove,
            leftIcon && styles.labelAboveWithLeftIcon
          )}
        >
          {label}
        </InputLabel>
        <div className={styles.inputContainer}>
          {leftIcon && (
            <div className={styles.leftIconContainer}>{leftIcon}</div>
          )}

          <label
            htmlFor={inputId}
            className={cx(
              styles.container,
              leftIcon && styles.containerWithLeftIcon,
              rightIcon && styles.containerWithRightIcon
            )}
            data-validation-state={validationState}
          >
            <Typography variant="Body/Paragraph/mdRegular">
              <AriaInput
                {...props}
                id={inputId}
                required={required}
                // Avoid duplicating label text in placeholder when label is positioned above
                // Screen readers would announce the label twice (once as label, once as placeholder)
                // Only use placeholder if explicitly provided, otherwise use empty string
                placeholder={placeholder ?? ''}
                className={cx(
                  styles.input,
                  styles.inputTopLabel,
                  props.className
                )}
                ref={internalRef}
              />
            </Typography>
          </label>
          {showClearContentIcon && hasValue && (
            <div className={styles.rightIconContainer}>
              <button
                type="button"
                className={styles.rightIconButton}
                onClick={onClearContent}
                // eslint-disable-next-line formatjs/no-literal-string-in-jsx
                aria-label="Clear content"
              >
                <MaterialIcon icon="cancel" />
              </button>
            </div>
          )}
          {rightIcon && !(showClearContentIcon && hasValue) && (
            <div className={styles.rightIconContainer}>{rightIcon}</div>
          )}
        </div>
      </>
    )
  }

  // Floating label (default behavior) - label inside container
  return (
    <div className={styles.inputContainer}>
      {leftIcon && <div className={styles.leftIconContainer}>{leftIcon}</div>}
      <AriaLabel
        className={cx(
          styles.container,
          leftIcon && styles.containerWithLeftIcon,
          rightIcon && styles.containerWithRightIcon
        )}
        data-validation-state={validationState}
      >
        <Typography variant="Body/Paragraph/mdRegular">
          <AriaInput
            {...props}
            id={id}
            required={required}
            // For floating labels, only set placeholder if explicitly provided
            // The label itself acts as the placeholder, so we don't want to duplicate it
            // This ensures the label only floats when focused or has value
            placeholder={placeholder}
            className={cx(styles.input, props.className)}
            ref={internalRef}
          />
        </Typography>
        <InputLabel required={required}>{label}</InputLabel>
      </AriaLabel>
      {showClearContentIcon && hasValue && (
        <div className={styles.rightIconContainer}>
          <button
            type="button"
            className={styles.rightIconButton}
            onClick={onClearContent}
            // eslint-disable-next-line formatjs/no-literal-string-in-jsx
            aria-label="Clear content"
          >
            <MaterialIcon icon="cancel" />
          </button>
        </div>
      )}
      {rightIcon && !(showClearContentIcon && hasValue) && (
        <div className={styles.rightIconContainer}>{rightIcon}</div>
      )}
    </div>
  )
})

export const Input = InputComponent as React.ForwardRefExoticComponent<
  InputProps & React.RefAttributes<HTMLInputElement>
>
