'use client';

import { useEffect, useId, useRef, useState } from 'react';
import { Input } from '@/components/ui/primitives/Input';
import { cn } from '@/lib/cn';

export interface OtpInputProps {
  value: string;
  onChange: (value: string) => void;
  onComplete?: (value: string) => void;
  length?: number;
  id?: string;
  name?: string;
  disabled?: boolean;
  invalid?: boolean;
  autoFocus?: boolean;
  className?: string;
  inputClassName?: string;
  getDigitAriaLabel?: (index: number) => string;
}

function normalizeDigits(value: string, length: number): string {
  return value.replace(/\D/g, '').slice(0, length);
}

export function OtpInput({
  value,
  onChange,
  onComplete,
  length = 6,
  id,
  name,
  disabled = false,
  invalid = false,
  autoFocus = false,
  className,
  inputClassName,
  getDigitAriaLabel,
}: OtpInputProps) {
  const baseId = useId();
  const rootId = id ?? `otp-input-${baseId}`;
  const inputRefs = useRef<Array<HTMLInputElement | null>>([]);
  const lastCompletedValueRef = useRef<string | null>(null);
  const normalizedValue = normalizeDigits(value, length);
  const digits = Array.from({ length }, (_, index) => normalizedValue[index] ?? '');

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

  useEffect(() => {
    if (normalizedValue.length !== length) {
      lastCompletedValueRef.current = null;
      return;
    }
    if (lastCompletedValueRef.current === normalizedValue) return;
    lastCompletedValueRef.current = normalizedValue;
    onComplete?.(normalizedValue);
  }, [length, normalizedValue, onComplete]);

  function focusIndex(index: number) {
    const nextIndex = Math.max(0, Math.min(index, length - 1));
    const nextInput = inputRefs.current[nextIndex];
    if (!nextInput) return;
    nextInput.focus();
    nextInput.select();
  }

  function commitDigits(nextDigits: string[], focusTarget?: number) {
    const nextValue = normalizeDigits(nextDigits.join(''), length);
    onChange(nextValue);
    if (typeof focusTarget === 'number') {
      requestAnimationFrame(() => {
        focusIndex(focusTarget);
      });
    }
  }

  function applySegment(rawValue: string, startIndex: number) {
    const segment = normalizeDigits(rawValue, length);
    if (!segment) {
      const nextDigits = [...digits];
      nextDigits[startIndex] = '';
      commitDigits(nextDigits);
      return;
    }

    const nextDigits = [...digits];
    for (let offset = 0; offset < segment.length && startIndex + offset < length; offset += 1) {
      nextDigits[startIndex + offset] = segment[offset] ?? '';
    }

    const nextIndex = Math.min(startIndex + segment.length, length - 1);
    commitDigits(nextDigits, nextIndex);
  }

  return (
    <div className={cn('flex items-center justify-center gap-2', className)} dir="ltr">
      {digits.map((digit, index) => (
        <Input
          key={`${rootId}-${index}`}
          ref={(node) => {
            inputRefs.current[index] = node;
          }}
          id={`${rootId}-${index + 1}`}
          name={name}
          type="text"
          inputMode="numeric"
          pattern="[0-9]*"
          autoComplete="one-time-code"
          enterKeyHint="done"
          dir="ltr"
          maxLength={length}
          value={digit}
          disabled={disabled}
          invalid={invalid}
          aria-label={getDigitAriaLabel?.(index) ?? `Digit ${index + 1}`}
          className={cn(
            'h-12 w-12 px-0 text-center text-lg font-semibold tabular-nums sm:h-14 sm:w-14',
            inputClassName,
          )}
          onChange={(event) => {
            applySegment(event.target.value, index);
          }}
          onKeyDown={(event) => {
            if (event.key === 'Backspace') {
              event.preventDefault();
              if (digits[index]) {
                const nextDigits = [...digits];
                nextDigits[index] = '';
                commitDigits(nextDigits);
                return;
              }
              if (index > 0) {
                const nextDigits = [...digits];
                nextDigits[index - 1] = '';
                commitDigits(nextDigits, index - 1);
              }
              return;
            }

            if (event.key === 'ArrowLeft') {
              event.preventDefault();
              focusIndex(index - 1);
              return;
            }

            if (event.key === 'ArrowRight') {
              event.preventDefault();
              focusIndex(index + 1);
            }
          }}
          onFocus={(event) => {
            event.currentTarget.select();
          }}
          onPaste={(event) => {
            event.preventDefault();
            applySegment(event.clipboardData.getData('text'), index);
          }}
        />
      ))}
    </div>
  );
}

export function OtpInputDemo() {
  const [value, setValue] = useState('');
  return <OtpInput value={value} onChange={setValue} />;
}
