import clsx from 'clsx';
import { forwardRef, FocusEvent, useState, Ref } from 'react';
import { Controller } from 'react-hook-form';
import { PatternFormat, PatternFormatProps } from 'react-number-format';
import { InputProps } from '@theme/components/types';
import { twMerge } from 'tailwind-merge';

const PatternFormatWithRef = forwardRef(
  (props: PatternFormatProps, ref: Ref<HTMLInputElement>) => {
    return <PatternFormat {...props} getInputRef={ref} />;
  }
);
PatternFormatWithRef.displayName = 'PatternFormatWithRef';

export const Input = forwardRef<
  HTMLInputElement,
  InputProps &
    Pick<
      PatternFormatProps,
      'mask' | 'allowEmptyFormatting' | 'onValueChange'
    > & {
      format?: string;
      defaultValue?: string;
      type?: string;
    }
>((props, ref) => {
  const [focused, setFocused] = useState(false);
  const [hasValue, setHasValue] = useState(false);
  const {
    id,
    label,
    labelStyle,
    error,
    mask,
    format,
    required = false,
    ...rest
  } = props;
  const hasFloatingLabel = label && labelStyle === 'floating';
  const inputClass = twMerge(
    clsx(
      'text-xs border px-2.5 h-10 placeholder:text-gray-600 peer',
      'focus-visible:outline-none', // disable outline on focus
      error
        ? 'border-error focus:border-error'
        : 'border-gray-500 hover:border-black focus:border-black'
    ),
    props.className
  );

  const inputProps: {
    id?: string;
    ref?: Ref<HTMLInputElement>;
    className?: string;
    onFocus?: () => void;
    onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
  } = {
    id,
    ref,
    className: inputClass,
    onFocus: () => setFocused(true),
    onBlur: (event: FocusEvent<HTMLInputElement>) => {
      setFocused(false);
      setHasValue(!!event.target.value);
    }
  };

  const Label = () => {
    if (!label) return null;

    return (
      <label
        htmlFor={id}
        className={twMerge(
          'text-xs text-gray-800 transition-all',
          clsx({
            'absolute left-2.5 pointer-events-none transform flex items-center h-full !top-0  peer-placeholder-shown:-translate-y-2 peer-placeholder-shown:bg-white peer-placeholder-shown:inline-flex peer-placeholder-shown:h-auto':
              hasFloatingLabel,
            'mb-2': !hasFloatingLabel,
            '-translate-y-2 bg-white inline-flex h-auto':
              hasFloatingLabel && (focused || hasValue)
          })
        )}
      >
        {label} {required && <span className="text-secondary">*</span>}
      </label>
    );
  };

  return (
    <div className="flex flex-col">
      <div className="relative flex flex-col">
        {props.format ? (
          <>
            {labelStyle !== 'floating' && <Label />}
            <Controller
              name={props.name ?? ''}
              control={props.control}
              render={({ field }) => (
                <PatternFormatWithRef
                  format={format}
                  mask={mask ?? ''}
                  {...rest}
                  {...field}
                  {...inputProps}
                  type={props.type as 'text' | 'password' | 'tel'}
                />
              )}
            />
            {labelStyle === 'floating' && <Label />}
          </>
        ) : (
          <>
            {labelStyle !== 'floating' && <Label />}
            <input {...rest} {...inputProps} />
            {labelStyle === 'floating' && <Label />}
          </>
        )}
      </div>
      {error && (
        <span className="mt-1 text-sm text-error">{String(error.message)}</span>
      )}
    </div>
  );
});

Input.displayName = 'Input';
