'use client';

import * as React from 'react';
import { cn } from '@/lib/utils';

export interface InputOTPProps {
  value: string;
  onChange: (value: string) => void;
  maxLength?: number;
  disabled?: boolean;
  autoFocus?: boolean;
  className?: string;
  onComplete?: (code: string) => void;
  children?: React.ReactNode;
}

interface InputOTPContextType {
  value: string;
  onChange: (value: string) => void;
  maxLength: number;
  disabled?: boolean;
  setFocusedIndex: (index: number) => void;
  setInputRef: (index: number, el: HTMLInputElement | null) => void;
  handleKeyDown: (index: number, e: React.KeyboardEvent<HTMLInputElement>) => void;
  handleChange: (index: number, e: React.ChangeEvent<HTMLInputElement>) => void;
  handlePaste: (e: React.ClipboardEvent<HTMLInputElement>) => void;
}

const InputOTPContext = React.createContext<InputOTPContextType | null>(null);

export function useInputOTP() {
  const context = React.useContext(InputOTPContext);
  if (!context) {
    throw new Error('useInputOTP must be used within an InputOTP');
  }
  return context;
}

export const InputOTP = React.forwardRef<HTMLDivElement, InputOTPProps>(
  (
    {
      value,
      onChange,
      maxLength = 4,
      disabled = false,
      autoFocus = true,
      className,
      onComplete,
      children,
    },
    ref
  ) => {
    const inputRefs = React.useRef<(HTMLInputElement | null)[]>([]);

    React.useEffect(() => {
      if (autoFocus && inputRefs.current[0]) {
        inputRefs.current[0]?.focus();
      }
    }, [autoFocus]);

    React.useEffect(() => {
      if (value.length === maxLength && onComplete) {
        onComplete(value);
      }
    }, [value, maxLength, onComplete]);

    const setInputRef = React.useCallback((index: number, el: HTMLInputElement | null) => {
      inputRefs.current[index] = el;
    }, []);

    const setFocusedIndex = React.useCallback((index: number) => {
      inputRefs.current[index]?.select();
    }, []);

    const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
      if (e.key === 'Backspace') {
        e.preventDefault();
        if (value[index]) {
          const chars = value.split('');
          chars[index] = '';
          onChange(chars.join(''));
        } else if (index > 0) {
          const chars = value.split('');
          chars[index - 1] = '';
          onChange(chars.join(''));
          inputRefs.current[index - 1]?.focus();
        }
      } else if (e.key === 'ArrowLeft') {
        e.preventDefault();
        if (index > 0) {
          inputRefs.current[index - 1]?.focus();
        }
      } else if (e.key === 'ArrowRight') {
        e.preventDefault();
        if (index < maxLength - 1) {
          inputRefs.current[index + 1]?.focus();
        }
      }
    };

    const handleChange = (index: number, e: React.ChangeEvent<HTMLInputElement>) => {
      const rawVal = e.target.value;
      const digit = rawVal.replace(/\D/g, '').slice(-1);

      if (!digit) {
        const chars = value.split('');
        chars[index] = '';
        onChange(chars.join(''));
        return;
      }

      const chars = value.padEnd(maxLength, ' ').split('');
      chars[index] = digit;
      const nextVal = chars.join('').trimEnd();
      onChange(nextVal);

      if (index < maxLength - 1) {
        inputRefs.current[index + 1]?.focus();
      }
    };

    const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
      e.preventDefault();
      const pastedData = e.clipboardData.getData('text/plain').replace(/\D/g, '').slice(0, maxLength);
      if (pastedData) {
        onChange(pastedData);
        const nextFocus = Math.min(pastedData.length, maxLength - 1);
        inputRefs.current[nextFocus]?.focus();
      }
    };

    return (
      <InputOTPContext.Provider
        value={{
          value,
          onChange,
          maxLength,
          disabled,
          setFocusedIndex,
          setInputRef,
          handleKeyDown,
          handleChange,
          handlePaste,
        }}
      >
        <div
          ref={ref}
          className={cn('flex items-center justify-center gap-3', className)}
        >
          {children}
        </div>
      </InputOTPContext.Provider>
    );
  }
);
InputOTP.displayName = 'InputOTP';

export const InputOTPGroup = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn('flex items-center gap-3', className)} {...props} />
));
InputOTPGroup.displayName = 'InputOTPGroup';

export interface InputOTPSlotProps extends React.HTMLAttributes<HTMLDivElement> {
  index: number;
}

export const InputOTPSlot = React.forwardRef<HTMLDivElement, InputOTPSlotProps>(
  ({ index, className, ...props }, ref) => {
    const {
      value,
      disabled,
      setInputRef,
      handleKeyDown,
      handleChange,
      handlePaste,
      setFocusedIndex,
    } = useInputOTP();

    const char = value[index] || '';

    return (
      <div
        ref={ref}
        className={cn('relative', className)}
        {...props}
      >
        <input
          ref={(el) => {
            setInputRef(index, el);
          }}
          type="text"
          inputMode="numeric"
          pattern="[0-9]*"
          maxLength={1}
          disabled={disabled}
          value={char}
          onFocus={() => setFocusedIndex(index)}
          onKeyDown={(e) => handleKeyDown(index, e)}
          onChange={(e) => handleChange(index, e)}
          onPaste={handlePaste}
          className={cn(
            'h-16 w-16 text-2xl font-bold text-center rounded-2xl border transition-all select-none outline-hidden',
            'border-zinc-200 bg-zinc-50/70 text-zinc-900 shadow-2xs',
            'focus:border-zinc-900 focus:bg-white focus:ring-2 focus:ring-zinc-900/10 focus:shadow-sm',
            'dark:border-zinc-800 dark:bg-zinc-900 dark:text-white',
            'dark:focus:border-white dark:focus:bg-zinc-800 dark:focus:ring-white/20',
            disabled && 'opacity-50 cursor-not-allowed'
          )}
          aria-label={`Digit ${index + 1}`}
        />
      </div>
    );
  }
);
InputOTPSlot.displayName = 'InputOTPSlot';
