import * as React from 'react';

import { Input } from '@/components/ui/input';
import { useLocale } from '@/hooks/use-locale';
import { cn } from '@/lib/utils';
import { __ } from '@/utils/i18n';

interface AmountInputProps {
    value: number;
    onChange: (valueInCents: number) => void;
    currencyCode: string;
    disabled?: boolean;
    required?: boolean;
    placeholder?: string;
    id?: string;
    className?: string;
    allowNegative?: boolean;
}

const getCurrencyInfo = (
    currencyCode: string,
    locale: string,
): { symbol: string; position: 'prefix' | 'suffix' } => {
    const parts = new Intl.NumberFormat(locale, {
        style: 'currency',
        currency: currencyCode,
    }).formatToParts(1);

    const symbolPart = parts.find((p) => p.type === 'currency');
    const symbol = symbolPart?.value ?? currencyCode;
    const symbolIndex = parts.findIndex((p) => p.type === 'currency');
    const literalIndex = parts.findIndex((p) => p.type === 'integer');
    const position = symbolIndex < literalIndex ? 'prefix' : 'suffix';

    return { symbol, position };
};

const formatCurrency = (value: number, locale: string): string => {
    const amount = value / 100;
    return new Intl.NumberFormat(locale, {
        minimumFractionDigits: 2,
        maximumFractionDigits: 2,
    }).format(amount);
};

const parseInputValue = (input: string): number => {
    const isNegative = input.trim().startsWith('-');
    const cleaned = input.replace(/[^\d.,]/g, '');

    if (!cleaned) {
        return 0;
    }

    const lastComma = cleaned.lastIndexOf(',');
    const lastDot = cleaned.lastIndexOf('.');

    let normalized: string;

    if (lastComma > lastDot) {
        normalized = cleaned.replace(/\./g, '').replace(',', '.');
    } else if (lastDot > lastComma) {
        normalized = cleaned.replace(/,/g, '');
    } else {
        normalized = cleaned.replace(',', '.');
    }

    const parsed = parseFloat(normalized);

    if (isNaN(parsed)) {
        return 0;
    }

    const cents = Math.round(parsed * 100);
    return isNegative ? -cents : cents;
};

const evaluateMathExpression = (input: string): number | null => {
    // Check for leading minus (negative result)
    const trimmed = input.trim();
    const isNegativeResult = trimmed.startsWith('-');
    const withoutLeadingMinus = isNegativeResult ? trimmed.substring(1) : trimmed;

    // Check if input contains math operators (excluding leading minus)
    if (!/[+\-*/]/.test(withoutLeadingMinus)) {
        return null; // No math operation found
    }

    try {
        // Remove spaces
        const cleaned = withoutLeadingMinus.replace(/\s/g, '');

        // Helper to convert parsed cents to dollars for calculation
        const parseToDollars = (str: string): number => {
            return parseInputValue(str) / 100;
        };

        // Split into tokens (numbers and operators)
        const tokens: (number | string)[] = [];
        let currentNumber = '';

        for (let i = 0; i < cleaned.length; i++) {
            const char = cleaned[i];
            if (['+', '-', '*', '/'].includes(char) && currentNumber) {
                tokens.push(parseToDollars(currentNumber));
                tokens.push(char);
                currentNumber = '';
            } else {
                currentNumber += char;
            }
        }
        if (currentNumber) {
            tokens.push(parseToDollars(currentNumber));
        }

        if (tokens.length < 3) {
            return null; // Need at least: number operator number
        }

        // Handle multiplication and division first (operator precedence)
        let i = 1;
        while (i < tokens.length) {
            if (tokens[i] === '*' || tokens[i] === '/') {
                const left = tokens[i - 1] as number;
                const op = tokens[i] as string;
                const right = tokens[i + 1] as number;

                const result = op === '*' ? left * right : left / right;
                tokens.splice(i - 1, 3, result);
            } else {
                i += 2;
            }
        }

        // Handle addition and subtraction
        let result = tokens[0] as number;
        for (let i = 1; i < tokens.length; i += 2) {
            const op = tokens[i] as string;
            const right = tokens[i + 1] as number;

            if (op === '+') {
                result += right;
            } else if (op === '-') {
                result -= right;
            }
        }

        // Apply negative sign if the input started with minus
        if (isNegativeResult) {
            result = -result;
        }

        return Math.round(result * 100);
    } catch {
        return null;
    }
};

const resolveCents = (input: string): number =>
    evaluateMathExpression(input) ?? parseInputValue(input);

export const AmountInput = React.forwardRef<HTMLInputElement, AmountInputProps>(
    (
        {
            value,
            onChange,
            currencyCode,
            disabled = false,
            required = false,
            placeholder = '0.00',
            id,
            className = '',
            allowNegative = false,
        },
        ref,
    ) => {
        const locale = useLocale();
        const [displayValue, setDisplayValue] = React.useState<string>('');
        const [isFocused, setIsFocused] = React.useState<boolean>(false);

        React.useEffect(() => {
            if (!isFocused) {
                if (value === 0) {
                    setDisplayValue('');
                } else {
                    setDisplayValue(formatCurrency(value, locale));
                }
            }
        }, [value, isFocused, locale]);

        const handleFocus = () => {
            setIsFocused(true);
            if (value !== 0) {
                const amount = (value / 100).toFixed(2);
                setDisplayValue(amount);
            } else if (!displayValue.startsWith('-')) {
                // Keep a lone '-' the user set via the toggle before typing.
                setDisplayValue('');
            }
        };

        const handleBlur = () => {
            setIsFocused(false);
            onChange(resolveCents(displayValue));
        };

        const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
            setDisplayValue(e.target.value);
        };

        const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.key === 'Enter') {
                onChange(resolveCents(displayValue));
            }
        };

        // iOS numeric keypads (inputMode="decimal") have no minus key, so
        // negative amounts need an explicit toggle. onClick handles both pointer
        // and keyboard; onPointerDown preventDefault keeps the input focused,
        // avoiding the blur/reformat race with the effect above.
        const toggleSign = () => {
            const next = displayValue.trim().startsWith('-')
                ? displayValue.replace('-', '')
                : `-${displayValue}`;
            setDisplayValue(next);
            onChange(resolveCents(next));
        };

        const isNegative = displayValue.trim().startsWith('-');

        const { symbol: currencySymbol, position: symbolPosition } = getCurrencyInfo(currencyCode, locale);

        return (
            <div className="relative">
                {symbolPosition === 'prefix' && (
                    <span
                        className={cn([
                            '-translate-y-1/2 absolute top-1/2 text-muted-foreground text-sm',
                            allowNegative ? 'left-10' : 'left-3',
                        ])}
                    >
                        {currencySymbol}
                    </span>
                )}
                <Input
                    ref={ref}
                    id={id}
                    type="text"
                    inputMode="decimal"
                    value={displayValue}
                    onChange={handleChange}
                    onFocus={handleFocus}
                    onBlur={handleBlur}
                    onKeyDown={handleKeyDown}
                    placeholder={placeholder}
                    disabled={disabled}
                    required={required}
                    className={cn([
                        'bg-background',
                        symbolPosition === 'suffix' && 'pr-9',
                        allowNegative
                            ? symbolPosition === 'prefix'
                                ? 'pl-14'
                                : 'pl-11'
                            : symbolPosition === 'prefix' && 'pl-9',
                        className,
                    ])}
                />
                {symbolPosition === 'suffix' && (
                    <span className="-translate-y-1/2 absolute top-1/2 right-3 text-muted-foreground text-sm">
                        {currencySymbol}
                    </span>
                )}
                {allowNegative && (
                    <button
                        type="button"
                        onPointerDown={(e) => e.preventDefault()}
                        onClick={toggleSign}
                        disabled={disabled}
                        aria-label={isNegative ? __('Make amount positive') : __('Make amount negative')}
                        aria-pressed={isNegative}
                        className={cn([
                            '-translate-y-1/2 absolute top-1/2 left-[0.35rem] flex h-7 w-7 items-center justify-center rounded-sm border font-semibold text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
                            isNegative
                                ? 'border-destructive/40 bg-destructive/10 text-destructive'
                                : 'border-input bg-muted text-foreground hover:bg-accent',
                        ])}
                    >
                        {isNegative ? '−' : '+'}
                    </button>
                )}
            </div>
        );
    },
);

AmountInput.displayName = 'AmountInput';

