import React, {
  ChangeEventHandler,
  forwardRef,
  InputHTMLAttributes,
  useEffect,
  useState,
} from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import { CFormControlWrapper, CFormControlWrapperProps } from '../form/CFormControlWrapper'

export interface CPasswordInputProps
  extends CFormControlWrapperProps,
    Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
  /**
   * Sets the accessible label (`aria-label`) for the toggle password visibility button. This improves accessibility for screen readers and should describe the action, e.g. `"Show password"` or `"Hide password"`.
   */
  ariaLabelToggler?: string

  /**
   * A string of all className you want applied to the component.
   */
  className?: string

  /**
   * Enables delayed `onChange` execution for improved performance in React password inputs.
   * - `true` delays the `onChange` callback by 500ms.
   * - You can also pass a custom delay (in milliseconds) as a number.
   */
  delay?: boolean | number

  /**
   * Disables the password input field. When `true`, the user cannot interact with the field.
   */
  disabled?: boolean

  /**
   * Callback triggered when the value of the password input changes. If `delay` is set, the callback is called after the specified timeout.
   */
  onChange?: ChangeEventHandler<HTMLInputElement>

  /**
   * Makes the React password input read-only. Prevents user input but allows text selection.
   */
  readOnly?: boolean

  /**
   * Controls the initial visibility of the password. When `true`, the input type is set to `"text"` instead of `"password"`. This allows **toggling password visibility** in React forms.
   */
  showPassword?: boolean

  /**
   * Sets the visual size of the password input. Use `'sm'` for small or `'lg'` for large input fields.
   */
  size?: 'sm' | 'lg'

  /**
   * The value of the password input field. To make the input controlled, pair this with the `onChange` handler.
   */
  value?: string | string[] | number
}

export const CPasswordInput = forwardRef<HTMLInputElement, CPasswordInputProps>(
  (
    {
      children,
      ariaLabelToggler = 'Toggle password visibility',
      className,
      delay = false,
      feedback,
      feedbackInvalid,
      feedbackValid,
      floatingClassName,
      floatingLabel,
      id,
      invalid,
      label,
      onChange,
      showPassword: showPasswordProp = false,
      size,
      text,
      tooltipFeedback,
      valid,
      ...rest
    },
    ref
  ) => {
    const [value, setValue] = useState<React.ChangeEvent<HTMLInputElement>>()
    const [showPassword, setShowPassword] = useState<boolean>(showPasswordProp)

    useEffect(() => {
      const timeOutId = setTimeout(
        () => value && onChange && onChange(value),
        typeof delay === 'number' ? delay : 500
      )

      return () => clearTimeout(timeOutId)
    }, [value])

    useEffect(() => {
      setShowPassword(showPasswordProp)
    }, [showPasswordProp])

    const renderInputAndButton = () => (
      <>
        <input
          className={classNames(
            'form-control',
            {
              [`form-control-${size}`]: size,
              'is-invalid': invalid,
              'is-valid': valid,
            },
            className
          )}
          id={id}
          type={showPassword ? 'text' : 'password'}
          onChange={(event) => (delay ? setValue(event) : onChange?.(event))}
          {...rest}
          ref={ref}
        >
          {children}
        </input>
        <button
          type="button"
          className="form-password-action"
          data-coreui-toggle="password"
          aria-label={ariaLabelToggler}
          onClick={() => setShowPassword((prev) => !prev)}
        >
          <span className="form-password-action-icon"></span>
        </button>
      </>
    )

    return (
      <CFormControlWrapper
        describedby={rest['aria-describedby']}
        feedback={feedback}
        feedbackInvalid={feedbackInvalid}
        feedbackValid={feedbackValid}
        floatingClassName={classNames('form-password', floatingClassName)}
        floatingLabel={floatingLabel}
        id={id}
        invalid={invalid}
        label={label}
        text={text}
        tooltipFeedback={tooltipFeedback}
        valid={valid}
      >
        {floatingLabel ? (
          renderInputAndButton()
        ) : (
          <div className="form-password">{renderInputAndButton()}</div>
        )}
      </CFormControlWrapper>
    )
  }
)

CPasswordInput.propTypes = {
  ariaLabelToggler: PropTypes.string,
  className: PropTypes.string,
  id: PropTypes.string,
  delay: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
  showPassword: PropTypes.bool,
  size: PropTypes.oneOf(['sm', 'lg']),
  ...CFormControlWrapper.propTypes,
}

CPasswordInput.displayName = 'CPasswordInput'
