import React, {
  ChangeEvent,
  Dispatch,
  KeyboardEvent,
  RefObject,
  SetStateAction,
  useCallback,
  useEffect,
  useMemo,
  useRef,
} from 'react';
import { twMerge } from 'tailwind-merge';

import { useBreakpointMd } from '@/hooks/useBreakpoint';

import { useChatInputStore } from '../../stores';

interface ExpandableTextareaProps {
  value: string;
  onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
  onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
  onStartTextAnimation?: () => void;
  onStopTextAnimation?: () => void;
  placeholder?: string;
  className?: string;
  inputClassName?: string;
  maxHeightPercentage?: number; // Max height as percentage of viewport height
  setIsComposing: Dispatch<SetStateAction<boolean>>;
  setIsChanged: Dispatch<SetStateAction<boolean>>;
  setIsSelected: Dispatch<SetStateAction<boolean>>;
  inputRef: RefObject<HTMLTextAreaElement | null>;
  lastCompositionEndRef: RefObject<number | null>;
  readOnly?: boolean;
  choices?: string[];
}

export const ExpandableTextarea: React.FC<ExpandableTextareaProps> = ({
  value,
  onChange,
  onKeyDown,
  placeholder = 'Ask me anything',
  className = '',
  inputClassName = '',
  maxHeightPercentage = 50,
  setIsComposing,
  setIsChanged,
  setIsSelected,
  inputRef,
  lastCompositionEndRef,
  readOnly = false,
  choices = [],
  onStartTextAnimation,
  onStopTextAnimation,
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const setInput = useChatInputStore((state) => state.setInput);
  const startTextAnimationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const isMobile = !useBreakpointMd();
  const hasAutoFocusedRef = useRef(false);

  const suggestion = useMemo(() => {
    return choices.find((choice: string) =>
      choice.toLowerCase().startsWith(value.toLowerCase())
    );
  }, [choices, value]);

  useEffect(() => {
    return () => {
      clearTimeout(startTextAnimationTimeoutRef.current ?? undefined);
      startTextAnimationTimeoutRef.current = null;
    };
  }, []);

  // Auto-focus only on desktop to avoid mobile keyboard/scroll issues
  useEffect(() => {
    if (
      !isMobile &&
      !readOnly &&
      inputRef.current &&
      !hasAutoFocusedRef.current
    ) {
      try {
        inputRef.current.focus({ preventScroll: true });
      } catch {
        // Fallback for browsers that don't support preventScroll
        const x = window.scrollX;
        const y = window.scrollY;
        inputRef.current.focus();
        window.scrollTo(x, y);
      }
      hasAutoFocusedRef.current = true;
    }
  }, [isMobile, readOnly, inputRef]);

  const handleChange = useCallback(
    (event: ChangeEvent<HTMLTextAreaElement>) => {
      // Update the data-value attribute for the sizing ghost element
      if (containerRef.current) {
        containerRef.current.dataset.value = event.target.value;
      }
      onChange(event);
    },
    [onChange]
  );

  const handleInput = useCallback(() => {
    setIsChanged(true);
    setIsSelected(false);
    onStopTextAnimation?.();
    requestAnimationFrame(() => {
      // Double-check the input is still empty before setting timeout
      if (inputRef.current?.value === '') {
        clearTimeout(startTextAnimationTimeoutRef.current ?? undefined);
        startTextAnimationTimeoutRef.current = setTimeout(() => {
          // Triple-check the input is still empty before starting animation
          if (inputRef.current?.value === '') {
            setIsChanged(false);
            onStartTextAnimation?.();
          }
          startTextAnimationTimeoutRef.current = null;
        }, 5000);
      } else {
        clearTimeout(startTextAnimationTimeoutRef.current ?? undefined);
        startTextAnimationTimeoutRef.current = null;
      }
    });
  }, [
    setIsChanged,
    onStartTextAnimation,
    onStopTextAnimation,
    inputRef,
    setIsSelected,
  ]);

  const handleKeyDown = useCallback(
    (event: KeyboardEvent<HTMLTextAreaElement>) => {
      if (event.key === 'Tab' && value !== suggestion && !!suggestion) {
        event.stopPropagation();
        event.preventDefault();
        setInput(suggestion);
      }
      onKeyDown?.(event);
    },
    [onKeyDown, suggestion, value, setInput]
  );

  return (
    <div
      ref={containerRef}
      data-value={value}
      className={twMerge(
        'relative inline-grid w-full min-w-0 !bg-transparent align-top',
        className
      )}
      style={{
        gridTemplateColumns: '1fr',
        gridTemplateRows: '1fr',
        maxHeight: `${maxHeightPercentage}vh`,
      }}
    >
      {/* Ghost element for suggestions */}
      <div className='pointer-events-none absolute top-0 left-0 z-[9999] px-1 py-0 leading-6 whitespace-pre-wrap text-foreground-primary/50'>
        <span className='opacity-0'>{value}</span>
        {value.trim().length > 0 ? (suggestion ?? '').slice(value.length) : ''}
      </div>

      {/* Ghost element for sizing */}
      <div
        className='invisible m-0 min-w-[1em] rounded-xl px-1 py-0 leading-6 whitespace-pre-wrap'
        style={{
          gridArea: '1 / 1',
          wordWrap: 'break-word',
          fontSize: 'inherit',
          fontFamily: 'inherit',
        }}
        aria-hidden='true'
      >
        {value + ' '}
      </div>

      {/* Actual textarea */}
      <textarea
        ref={inputRef}
        value={value}
        readOnly={readOnly}
        onChange={handleChange}
        onInput={handleInput}
        onKeyDown={handleKeyDown}
        rows={1}
        placeholder={placeholder}
        autoComplete='off'
        name='prompt'
        className={twMerge(
          'm-0 w-full min-w-[1em] resize-none appearance-none overflow-y-auto border-none !bg-transparent leading-6 outline-none',
          inputClassName
        )}
        style={{
          gridArea: '1 / 1',
          fontSize: 'inherit',
          fontFamily: 'inherit',
          maxHeight: `${maxHeightPercentage}vh`,
        }}
        onCompositionStart={() => setIsComposing(true)}
        onCompositionEnd={() => {
          lastCompositionEndRef.current = Date.now();
          setIsComposing(false);
        }}
      />
    </div>
  );
};
