'use client';

import { useEffect, useRef, useState } from 'react';

import { Input } from '@/components/ui/input';
import { CountryCombobox } from '@/components/shared/country-combobox';
import { cn } from '@/lib/utils';
import { callingCodeFor, formatAsYouType, parsePhone } from '@/lib/i18n/phone';
import { detectDefaultCountry, type CountryCode } from '@/lib/i18n/countries';

export interface PhoneInputValue {
  /** E.164 form ('+442079460958'). Null when empty or unparseable. */
  e164: string | null;
  /** Country selected in the dropdown - drives the AsYouType formatter. */
  country: CountryCode;
}

interface PhoneInputProps {
  value: PhoneInputValue | null | undefined;
  onChange: (next: PhoneInputValue) => void;
  /** Pre-selects the dropdown when `value.country` isn't supplied. */
  defaultCountry?: CountryCode;
  placeholder?: string;
  disabled?: boolean;
  required?: boolean;
  invalid?: boolean;
  id?: string;
  'data-testid'?: string;
}

/**
 * Phone input with a country flag dropdown + format-as-you-type.
 *
 * Wire shape: emits `{ e164, country }` on every change. E.164 is null
 * while the input is too short to parse - that's a form-validation
 * concern, not an input concern. Pasting an international number
 * (`+1 415…`) auto-switches the country dropdown to match.
 *
 * Implementation notes:
 *   - The visible string is always the AsYouType national-format,
 *     so users see e.g. "020 7946 0958" while typing GB digits.
 *   - The `country` prop drives the AsYouType context; flipping
 *     countries reformats the same digits against the new country
 *     (matches what users expect when they fix a wrong country pick).
 */
export function PhoneInput({
  value,
  onChange,
  defaultCountry,
  placeholder = 'Phone number',
  disabled,
  required,
  invalid,
  id,
  'data-testid': testId,
}: PhoneInputProps) {
  const [country, setCountry] = useState<CountryCode>(
    () => value?.country ?? defaultCountry ?? detectDefaultCountry('US'),
  );
  const [display, setDisplay] = useState(() => {
    if (!value?.e164) return '';
    const parsed = parsePhone(value.e164, value.country);
    return parsed.national ?? '';
  });
  // Track whether the user has typed since mount - keeps a controlled-from-props
  // value sync on first render only.
  const initialized = useRef(false);

  useEffect(() => {
    if (initialized.current) return;
    initialized.current = true;
  }, []);

  function emit(rawDigits: string, currentCountry: CountryCode) {
    const parsed = parsePhone(rawDigits, currentCountry);
    onChange({ e164: parsed.e164, country: currentCountry });
  }

  function handleInput(raw: string) {
    // Paste-detect: if user pasted an international format, parse it
    // and flip the country dropdown to match - better UX than asking
    // them to also click the dropdown.
    if (raw.startsWith('+')) {
      const parsed = parsePhone(raw);
      if (parsed.country) {
        setCountry(parsed.country);
        const reformatted = formatAsYouType(raw, parsed.country);
        setDisplay(reformatted);
        emit(raw, parsed.country);
        return;
      }
    }
    const formatted = formatAsYouType(raw, country);
    setDisplay(formatted);
    emit(formatted, country);
  }

  function handleCountryChange(next: CountryCode | null) {
    if (!next) return;
    setCountry(next);
    // Re-run the formatter against the new country so the visible
    // string stays consistent with the dropdown.
    const reformatted = formatAsYouType(display, next);
    setDisplay(reformatted);
    emit(reformatted, next);
  }

  return (
    <div
      className={cn(
        // `w-full` keeps the row matching the field width below it instead
        // of collapsing to its content's intrinsic width when nested inside
        // a flex/grid cell.
        'flex w-full items-stretch gap-1.5',
        invalid && '[&_input]:border-destructive [&_button[role=combobox]]:border-destructive',
      )}
    >
      <CountryCombobox
        value={country}
        onChange={handleCountryChange}
        compact
        clearable={false}
        disabled={disabled}
        data-testid={testId ? `${testId}-country` : undefined}
      />
      <div className="flex flex-1 items-stretch overflow-hidden rounded-md border border-input focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background">
        <span className="flex select-none items-center bg-muted/40 px-2 text-sm tabular-nums text-muted-foreground">
          {callingCodeFor(country)}
        </span>
        <Input
          id={id}
          type="tel"
          inputMode="tel"
          autoComplete="tel"
          placeholder={placeholder}
          value={display}
          disabled={disabled}
          required={required}
          onChange={(e) => handleInput(e.target.value)}
          // Strip the inner Input's own border so it sits flush with the country prefix.
          className="flex-1 border-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
          data-testid={testId}
        />
      </div>
    </div>
  );
}
