import { useRef, useState } from 'react';
import {
    InputGroup,
    InputGroupAddon,
    InputGroupInput,
    InputGroupText,
} from '@/components/ui/input-group';

type RupiahInputProps = {
    name: string;
    id?: string;
    defaultValue?: string | number | null;
    placeholder?: string;
    disabled?: boolean;
    ariaInvalid?: boolean;
};

// Strips everything but digits from a display value (e.g. "1.500.000" -> "1500000").
function digitsOnly(value: string): string {
    return value.replace(/\D/g, '').replace(/^0+(?=\d)/, '');
}

// Parses the initial value coming from the backend, which may be a decimal
// string like "3000000.00" - only the integer part before the decimal point
// is kept (unlike digitsOnly, which would wrongly treat "." as a thousands
// separator here).
function parseInitialDigits(value: string | number | null | undefined): string {
    return digitsOnly(String(value ?? '').split('.')[0]);
}

function formatThousands(digits: string): string {
    return digits.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}

// Counts digit characters (ignoring separators) before `caret` in `value`.
function digitsBeforeCaret(value: string, caret: number): number {
    let count = 0;

    for (let i = 0; i < caret && i < value.length; i++) {
        if (/\d/.test(value[i])) {
            count++;
        }
    }

    return count;
}

// Finds the position in `formatted` right after the `digitCount`-th digit.
function caretForDigitCount(formatted: string, digitCount: number): number {
    if (digitCount <= 0) {
        return 0;
    }

    let seen = 0;

    for (let i = 0; i < formatted.length; i++) {
        if (/\d/.test(formatted[i])) {
            seen++;

            if (seen === digitCount) {
                return i + 1;
            }
        }
    }

    return formatted.length;
}

/**
 * The visible <input>'s value IS the formatted "1.000.000" text - there is
 * no separate overlay, so the caret can never visually drift from the
 * displayed digits. On every change, the caret's digit-position is measured
 * first, the value is reformatted, and the caret is restored to the same
 * digit-position - all synchronously inside the change handler (never via
 * useLayoutEffect or requestAnimationFrame), so the DOM is already
 * consistent by the time React commits and fast typing never drops
 * keystrokes.
 */
export function RupiahInput({
    name,
    id,
    defaultValue,
    placeholder = '0',
    disabled,
    ariaInvalid,
}: RupiahInputProps) {
    const hiddenRef = useRef<HTMLInputElement>(null);
    const initialDigits = parseInitialDigits(defaultValue);
    const [value, setValue] = useState(formatThousands(initialDigits));

    function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
        const input = e.target;
        const caret = input.selectionStart ?? input.value.length;
        const digitCount = digitsBeforeCaret(input.value, caret);

        const digits = digitsOnly(input.value);
        const formatted = formatThousands(digits);
        const newCaret = caretForDigitCount(formatted, digitCount);

        input.value = formatted;
        input.setSelectionRange(newCaret, newCaret);

        setValue(formatted);

        if (hiddenRef.current) {
            hiddenRef.current.value = digits;
        }
    }

    return (
        <InputGroup>
            <InputGroupAddon>
                <InputGroupText>Rp</InputGroupText>
            </InputGroupAddon>
            <InputGroupInput
                id={id}
                inputMode="numeric"
                placeholder={placeholder}
                value={value}
                onChange={handleChange}
                disabled={disabled}
                aria-invalid={ariaInvalid}
            />
            <input
                ref={hiddenRef}
                type="hidden"
                name={name}
                defaultValue={initialDigits}
            />
        </InputGroup>
    );
}
