'use client';

import * as React from 'react';

import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';
import { currencySymbol } from '@/lib/utils/currency';

interface CurrencyInputProps extends Omit<
  React.ComponentProps<'input'>,
  'value' | 'onChange' | 'type'
> {
  /** Controlled raw numeric value. `null` / `undefined` render empty. */
  value: number | string | null | undefined;
  /** Fires with a raw number (or `null` if cleared). */
  onChange: (value: number | null) => void;
  /** ISO currency code; renders as a leading symbol prefix. */
  currency?: string;
  className?: string;
}

const groupFormatter = new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 2,
  useGrouping: true,
});

function formatGrouped(value: number | string): string {
  const n = typeof value === 'number' ? value : Number(value);
  if (!Number.isFinite(n)) return '';
  return groupFormatter.format(n);
}

function parseTyped(raw: string): { display: string; numeric: number | null } {
  // Strip everything except digits, '.', '-'. Commas are formatting noise from
  // our own display and are removed before re-grouping. (Locale note: this
  // assumes '.' as decimal separator, matching the en-US formatter below.)
  let cleaned = raw.replace(/[^\d.-]/g, '');
  // Keep only the first '.' (additional dots are dropped).
  const firstDot = cleaned.indexOf('.');
  if (firstDot !== -1) {
    cleaned = cleaned.slice(0, firstDot + 1) + cleaned.slice(firstDot + 1).replace(/\./g, '');
  }
  // Sign: only honour a leading '-'; strip any others.
  const negative = cleaned.startsWith('-');
  cleaned = (negative ? '-' : '') + cleaned.replace(/-/g, '');

  if (cleaned === '' || cleaned === '-') return { display: cleaned, numeric: null };

  const dot = cleaned.indexOf('.');
  const intPart = dot === -1 ? cleaned : cleaned.slice(0, dot);
  const fracPart = dot === -1 ? null : cleaned.slice(dot + 1);
  const intDigitsOnly = intPart.replace('-', '');
  const intNumeric = intDigitsOnly === '' ? 0 : Number(intDigitsOnly);
  const numeric = (negative ? -1 : 1) * (intNumeric + (fracPart ? Number(`0.${fracPart}`) || 0 : 0));

  const intDisplay =
    intDigitsOnly === ''
      ? (negative ? '-' : '')
      : (negative ? '-' : '') + groupFormatter.format(intNumeric);
  const display = fracPart === null ? intDisplay : `${intDisplay}.${fracPart}`;

  return { display, numeric: Number.isFinite(numeric) ? numeric : null };
}

/**
 * Numeric input pre-decorated with a currency symbol and thousand-separator
 * grouping (e.g. `3,528,000.50`). Uses `type="text"` + `inputMode="decimal"`
 * so we can render commas (HTML `type="number"` strips them) while still
 * surfacing the decimal keypad on iOS/Android. The parent receives a raw
 * number via `onChange`; the formatted string is local UI state.
 */
export const CurrencyInput = React.forwardRef<HTMLInputElement, CurrencyInputProps>(
  ({ value, onChange, currency = 'USD', className, onBlur, onFocus, ...props }, ref) => {
    const symbol = currencySymbol(currency);

    const [display, setDisplay] = React.useState<string>(() =>
      value === null || value === undefined || value === '' ? '' : formatGrouped(value),
    );
    const focusedRef = React.useRef(false);

    // Re-sync the display when the controlled value changes externally (form
    // reset, parent-driven update). Skip while the input is focused so we
    // don't fight the user's keystrokes.
    React.useEffect(() => {
      if (focusedRef.current) return;
      if (value === null || value === undefined || value === '') {
        setDisplay('');
      } else {
        setDisplay(formatGrouped(value));
      }
    }, [value]);

    return (
      <div className="relative">
        <span
          className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground"
          aria-hidden
        >
          {symbol}
        </span>
        <Input
          ref={ref}
          type="text"
          inputMode="decimal"
          autoComplete="off"
          value={display}
          onChange={(e) => {
            const { display: nextDisplay, numeric } = parseTyped(e.target.value);
            setDisplay(nextDisplay);
            onChange(numeric);
          }}
          onFocus={(e) => {
            focusedRef.current = true;
            onFocus?.(e);
          }}
          onBlur={(e) => {
            focusedRef.current = false;
            // On blur, canonicalize to a clean grouped representation so the
            // user sees the final value rather than any half-typed state.
            if (value === null || value === undefined || value === '') {
              setDisplay('');
            } else {
              setDisplay(formatGrouped(value));
            }
            onBlur?.(e);
          }}
          className={cn('pl-9 tabular-nums', className)}
          {...props}
        />
      </div>
    );
  },
);
CurrencyInput.displayName = 'CurrencyInput';
