import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import styled from 'styled-components';
import { useLocale } from '@/app/providers/LocaleProvider';

const OTP_LENGTH = 6;

const Fieldset = styled.fieldset`
  border: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: ${({ theme }) => theme.spacing[2]};
`;

const Label = styled.legend`
  font-size: ${({ theme }) => theme.typography.fontSize.sm};
  font-weight: ${({ theme }) => theme.typography.fontWeight.medium};
  color: ${({ theme }) => theme.colors.text.primary};
  text-align: center;
  width: 100%;
  padding: 0;
`;

const InputsRow = styled.div`
  display: flex;
  justify-content: center;
  gap: ${({ theme }) => theme.spacing[2]};
  direction: ltr;
`;

const DigitInput = styled.input<{ $hasError?: boolean }>`
  width: 2.75rem;
  height: 3.25rem;
  text-align: center;
  font-size: ${({ theme }) => theme.typography.fontSize.xl};
  font-weight: ${({ theme }) => theme.typography.fontWeight.semibold};
  border: 1px solid
    ${({ theme, $hasError }) => ($hasError ? theme.colors.error : theme.colors.border)};
  border-radius: ${({ theme }) => theme.borderRadius.md};
  background-color: ${({ theme }) => theme.colors.background};
  color: ${({ theme }) => theme.colors.text.primary};
  outline: none;
  transition: border-color ${({ theme }) => theme.transitions.fast};

  &:focus {
    border-color: ${({ theme, $hasError }) =>
      $hasError ? theme.colors.error : theme.colors.primary};
    box-shadow: 0 0 0 2px
      ${({ theme, $hasError }) => ($hasError ? `${theme.colors.error}33` : `${theme.colors.primary}33`)};
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
`;

const VisuallyHidden = styled.input`
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
`;

export interface OtpInputProps {
  value: string;
  onChange: (value: string) => void;
  label?: string;
  disabled?: boolean;
  hasError?: boolean;
  autoFocus?: boolean;
  'data-testid'?: string;
}

export const OtpInput: React.FC<OtpInputProps> = ({
  value,
  onChange,
  label,
  disabled = false,
  hasError = false,
  autoFocus = false,
  'data-testid': dataTestId,
}) => {
  const { t } = useLocale();
  const inputRefs = useRef<Array<HTMLInputElement | null>>([]);

  const digits = useMemo(() => {
    const normalized = value.replace(/\D/g, '').slice(0, OTP_LENGTH);
    return Array.from({ length: OTP_LENGTH }, (_, index) => normalized[index] ?? '');
  }, [value]);

  const focusIndex = useCallback((index: number) => {
    const clamped = Math.max(0, Math.min(OTP_LENGTH - 1, index));
    inputRefs.current[clamped]?.focus();
    inputRefs.current[clamped]?.select();
  }, []);

  const applyDigits = useCallback(
    (nextDigits: string[]) => {
      onChange(nextDigits.join('').slice(0, OTP_LENGTH));
    },
    [onChange],
  );

  useEffect(() => {
    if (autoFocus) {
      const firstEmpty = digits.findIndex((digit) => digit === '');
      focusIndex(firstEmpty === -1 ? OTP_LENGTH - 1 : firstEmpty);
    }
  }, [autoFocus, digits, focusIndex]);

  const handleDigitChange = (index: number, nextChar: string) => {
    const digit = nextChar.replace(/\D/g, '').slice(-1);
    const next = [...digits];
    next[index] = digit;
    applyDigits(next);

    if (digit && index < OTP_LENGTH - 1) {
      focusIndex(index + 1);
    }
  };

  const handleKeyDown = (index: number, event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'Backspace' && !digits[index] && index > 0) {
      event.preventDefault();
      focusIndex(index - 1);
      return;
    }

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

    if (event.key === 'ArrowRight' && index < OTP_LENGTH - 1) {
      event.preventDefault();
      focusIndex(index + 1);
    }
  };

  const handlePaste = (event: React.ClipboardEvent<HTMLInputElement>) => {
    event.preventDefault();
    const pasted = event.clipboardData.getData('text').replace(/\D/g, '').slice(0, OTP_LENGTH);
    if (!pasted) {
      return;
    }

    const next = Array.from({ length: OTP_LENGTH }, (_, index) => pasted[index] ?? '');
    applyDigits(next);
    focusIndex(Math.min(pasted.length, OTP_LENGTH - 1));
  };

  const handleAutofill = (event: React.ChangeEvent<HTMLInputElement>) => {
    const autofill = event.target.value.replace(/\D/g, '').slice(0, OTP_LENGTH);
    if (!autofill) {
      return;
    }
    applyDigits(Array.from({ length: OTP_LENGTH }, (_, index) => autofill[index] ?? ''));
    focusIndex(Math.min(autofill.length, OTP_LENGTH - 1));
  };

  return (
    <Fieldset data-testid={dataTestId}>
      {label && <Label>{label}</Label>}
      <VisuallyHidden
        type="text"
        inputMode="numeric"
        autoComplete="one-time-code"
        tabIndex={-1}
        aria-hidden
        value={value}
        onChange={handleAutofill}
      />
      <InputsRow>
        {digits.map((digit, index) => (
          <DigitInput
            key={index}
            ref={(element) => {
              inputRefs.current[index] = element;
            }}
            type="text"
            inputMode="numeric"
            pattern="[0-9]*"
            maxLength={1}
            value={digit}
            disabled={disabled}
            $hasError={hasError}
            autoComplete={index === 0 ? 'one-time-code' : 'off'}
            aria-label={t('auth:login.otpDigitAria', { index: index + 1, total: OTP_LENGTH })}
            onChange={(event) => handleDigitChange(index, event.target.value)}
            onKeyDown={(event) => handleKeyDown(index, event)}
            onPaste={handlePaste}
            onFocus={(event) => event.target.select()}
          />
        ))}
      </InputsRow>
    </Fieldset>
  );
};
