import {
  type CSSProperties,
  type RefObject,
  useCallback,
  useEffect,
  useLayoutEffect,
  useState,
} from 'react';
import { Props as ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';

type InputStyleObject = {
  value: string;
  focus: boolean;
  inputElement: RefObject<HTMLInputElement> | null;
} & Pick<ExpressiveMoneyInputProps, 'loading'>;

export const useInputStyle = ({ value, focus, inputElement, loading }: InputStyleObject) => {
  const initialRender = !useTimeout(300);
  const inputWidth = useFirstDefinedValue(inputElement?.current?.clientWidth, [
    inputElement?.current,
    value,
  ]);

  const getStyle = useCallback((): CSSProperties => {
    const fontSize = getFontSize(value, focus, inputWidth);

    return {
      fontSize,
      height: fontSize,
      // aligns the top of the digit with the currency button
      marginTop: fontSize * -0.19,
      // if the input loads with a pre-filled value, we don't want to animate the font-size immediately
      transition: initialRender ? 'none' : undefined,
      color: loading ? 'var(--color-interactive-secondary)' : undefined,
    };
  }, [value, focus, inputWidth, loading, initialRender]);

  const [style, setStyle] = useState<CSSProperties>(() => getStyle());

  useLayoutEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect -- Computing style based on layout measurements
    setStyle(getStyle());
  }, [value, focus, loading, inputWidth, getStyle]);

  return style;
};

function getFontSize(inputValue: string, isFocused: boolean, inputWidth: number | undefined) {
  const defaultFontSize = 52;
  const focusFontSize = 90;
  const minimumFontSize = 34;

  let fontSize = isFocused ? focusFontSize : defaultFontSize;

  if (typeof inputWidth === 'undefined') {
    return fontSize;
  }
  const textLength = inputValue.length;
  const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);

  if (textLength > maxCharactersWithoutShrinking) {
    const adjustedSize = Math.round((inputWidth / textLength) * 1.9);
    fontSize = Math.min(fontSize, adjustedSize);
  }

  return Math.max(fontSize, minimumFontSize);
}

const useFirstDefinedValue = (newValue: number | undefined, dependencies: unknown[]) => {
  const [value, setValue] = useState<number | undefined>(newValue);

  useLayoutEffect(() => {
    if (typeof newValue !== 'undefined' && typeof value === 'undefined') {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- Lazy initialization from prop
      setValue(newValue);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [...dependencies, value, newValue]);

  return value;
};

const useTimeout = (delay: number) => {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const timeout = setTimeout(() => {
      setReady(true);
    }, delay);

    return () => {
      clearTimeout(timeout);
    };
  }, [delay]);

  return ready;
};
