import type { DetailedHTMLProps } from 'react';
import type { Responsive } from '@radix-ui/themes/props';
import { Box } from '@radix-ui/themes';
import React, {
  forwardRef,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

export interface AutoSizingInputProps
  extends DetailedHTMLProps<
    React.InputHTMLAttributes<HTMLInputElement>,
    HTMLInputElement
  > {
  value?: string;
  onChangeValue?: (value: string) => void;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  maxWidth?: Responsive<string>;
  minWidth?: Responsive<string>;
}

// Canvas를 이용한 텍스트 너비 측정 훅
const useTextWidth = (text: string, font: string): number => {
  const [textWidth, setTextWidth] = useState(0);

  useEffect(() => {
    if (!text) {
      setTextWidth(0);
      return;
    }

    // 클라이언트 사이드에서만 실행
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    if (!context) {
      setTextWidth(0);
      return;
    }

    context.font = font;
    const width = Math.ceil(context.measureText(text).width);
    setTextWidth(width);
  }, [text, font]);

  return textWidth;
};

const getFontString = (element: HTMLElement | null): string => {
  if (!element) return '14px sans-serif'; // 기본값
  
  // SSR 환경에서는 window가 없으므로 기본값 반환
  if (typeof window === 'undefined') return '14px sans-serif';

  const computedStyle = window.getComputedStyle(element);
  const fontSize = computedStyle.fontSize || '14px';
  const fontFamily = computedStyle.fontFamily || 'sans-serif';
  const fontWeight = computedStyle.fontWeight || 'normal';
  const fontStyle = computedStyle.fontStyle || 'normal';

  return `${fontStyle} ${fontWeight} ${fontSize} ${fontFamily}`;
};

export const AutoSizingInput = forwardRef<
  HTMLInputElement,
  AutoSizingInputProps
>(
  (
    {
      value: externalValue,
      onChangeValue,
      onChange,
      maxWidth,
      minWidth,
      ...rest
    },
    ref
  ): React.ReactNode => {
    const isControlled = externalValue !== undefined;
    const [internalValue, setInternalValue] = useState(externalValue || '');
    const value = isControlled ? externalValue : internalValue;

    const defaultRef = useRef<HTMLInputElement>(null);
    const inputRef = ref || defaultRef;
    const [fontString, setFontString] = useState('14px sans-serif');

    const displayValue = useMemo(
      () => value || rest.placeholder || '',
      [value, rest.placeholder]
    );

    const textWidth = useTextWidth(displayValue, fontString);

    const dynamicWidth = useMemo(
      () => `${Math.max(textWidth + 16, 24)}px`,
      [textWidth]
    );

    useEffect(() => {
      if (!('current' in inputRef) || !inputRef.current) return;
      const element = inputRef.current;
      const updateFont = (): void => {
        const font = getFontString(element);
        setFontString(font);
      };

      updateFont();
 
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (
            mutation.type === 'attributes' &&
            (mutation.attributeName === 'style' ||
              mutation.attributeName === 'class')
          ) {
            updateFont();
          }
        });
      });

      observer.observe(element, {
        attributes: true,
        attributeFilter: ['style', 'class'],
      });

      return () => {
        observer.disconnect();
      };
    }, [inputRef]);

    // 단일 변경 핸들러로 통합
    const handleChange = useCallback<
      React.ChangeEventHandler<HTMLInputElement>
    >(
      (evt) => {
        const newValue = evt.target.value;
 
        onChange?.(evt);
 
        if (!isControlled) {
          setInternalValue(newValue);
        }
 
        onChangeValue?.(newValue);
      },
      [onChange, onChangeValue, isControlled]
    );

    const onClick = useCallback(() => {
      if ('current' in inputRef) {
        inputRef.current?.focus();
      }
    }, [inputRef]);

    return (
      <Box
        className="auto-sizing-input wrapper"
        maxWidth={maxWidth}
        minWidth={minWidth}
        onClick={onClick}
        style={{ width: dynamicWidth }}
      >
        <input onChange={handleChange} ref={inputRef} value={value} {...rest} />
      </Box>
    );
  }
);

AutoSizingInput.displayName = 'AutoSizingInput';
