import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Group, Select, Text, TextInput, type ComboboxItem, type SelectProps } from '@mantine/core';
import {
  AsYouType,
  getCountries,
  getCountryCallingCode,
  parsePhoneNumberFromString,
  type CountryCode,
} from 'libphonenumber-js';
import { CountryFlag, getCountryName, resolveLang } from './CountrySelect';
import { nextNationalDigits, toE164, toNationalDigits } from './phone';

// Static list, computed once at module load — same as CountrySelect's dataset.
const COUNTRY_CODES = getCountries();

type CountryOption = ComboboxItem & { name: string };

// Search by country name ("united"), dial code ("+1") or ISO prefix ("us") —
// the closed control's label alone ("+1") isn't enough to find a country.
const filterCountries: SelectProps['filter'] = ({ options, search }) => {
  const q = search.trim().toLowerCase();
  if (!q) return options;
  return (options as CountryOption[]).filter(
    (o) =>
      o.name.toLowerCase().includes(q) ||
      o.label.toLowerCase().includes(q) ||
      o.value.toLowerCase().startsWith(q),
  );
};

const renderCountryOption: SelectProps['renderOption'] = ({ option }) => {
  const o = option as CountryOption;
  return (
    <Group gap="xs" wrap="nowrap" justify="space-between" flex={1}>
      <Group gap="xs" wrap="nowrap">
        <CountryFlag code={o.value} />
        <Text fz="sm">{o.name}</Text>
      </Group>
      <Text fz="xs" c="dimmed">{o.label}</Text>
    </Group>
  );
};

export interface PhoneInputProps {
  /** E.164 (`+14155552671`), or '' when empty. */
  value: string;
  onChange: (value: string) => void;
  /** Fired when the number box loses focus — wire to the form's `trigger`. */
  onBlur?: () => void;
  label?: React.ReactNode;
  placeholder?: string;
  description?: React.ReactNode;
  error?: React.ReactNode;
  required?: boolean;
  /** Mantine's asterisk-without-`required` variant, as used by config-driven forms. */
  withAsterisk?: boolean;
  disabled?: boolean;
  readOnly?: boolean;
}

/**
 * International phone entry: a searchable country/dial-code select beside a
 * national-number text box, WhatsApp/Telegram style. Controlled —
 * `value`/`onChange` carry the E.164 string.
 *
 * The typed digits live in local state rather than being re-derived from
 * `value` on every render: an incomplete number doesn't parse, so deriving
 * would blank the box between keystrokes.
 */
export function PhoneInput({
  value,
  onChange,
  onBlur,
  label,
  placeholder,
  description,
  error,
  required,
  withAsterisk,
  disabled,
  readOnly,
}: PhoneInputProps) {
  const { t, i18n } = useTranslation();
  const lang = resolveLang(i18n.language);

  const initialCountry = parsePhoneNumberFromString(value || '')?.country ?? 'ET';
  const [country, setCountry] = useState<CountryCode>(initialCountry);
  const [national, setNational] = useState(() =>
    new AsYouType(initialCountry).input(toNationalDigits(value, initialCountry)),
  );

  // What this field last pushed upward, so an echo of our own value isn't
  // mistaken for the parent resetting the form.
  const emitted = useRef(value);

  useEffect(() => {
    if (value === emitted.current) return;
    const parsedCountry = parsePhoneNumberFromString(value || '')?.country;
    const next = parsedCountry ?? country;
    if (parsedCountry) setCountry(parsedCountry);
    setNational(new AsYouType(next).input(toNationalDigits(value, next)));
    emitted.current = value;
    // Adopting an outside change only — `country` is state written here, so
    // re-running on it would fight the user's typing.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value]);

  const countryOptions = useMemo<CountryOption[]>(
    () =>
      COUNTRY_CODES.map((code) => ({
        value: code,
        label: `+${getCountryCallingCode(code)}`,
        name: getCountryName(code, lang),
      })).sort((a, b) => a.name.localeCompare(b.name, lang)),
    [lang],
  );

  function push(next: string) {
    emitted.current = next;
    onChange(next);
  }

  function applyDigits(digits: string, forCountry: CountryCode) {
    // Once the digits parse, show the true national number: someone who
    // types a trunk prefix (0911111111) or the country code (251911111111)
    // shouldn't end up with it doubled beside the "+251" selector.
    const parsed = parsePhoneNumberFromString(digits, forCountry);
    setNational(new AsYouType(forCountry).input(parsed?.nationalNumber ?? digits));
    push(parsed?.number ?? toE164(digits, forCountry));
  }

  function handleText(text: string) {
    // A full `+<code><number>` arriving at once (paste, autofill, or a test
    // driver's `.fill()`) is parsed standalone and switches the country,
    // rather than being read under whatever country was already selected.
    const trimmed = text.trim();
    if (trimmed.startsWith('+')) {
      const parsed = parsePhoneNumberFromString(trimmed);
      if (parsed?.country) {
        setCountry(parsed.country);
        setNational(new AsYouType(parsed.country).input(parsed.nationalNumber));
        push(parsed.number);
        return;
      }
    }
    applyDigits(nextNationalDigits(text, national), country);
  }

  function handleCountryChange(next: string | null) {
    if (!next) return;
    const code = next as CountryCode;
    setCountry(code);
    applyDigits(national.replace(/\D/g, ''), code);
  }

  return (
    <TextInput
      type="tel"
      inputMode="tel"
      autoComplete="tel"
      label={label}
      description={description}
      error={error}
      required={required}
      withAsterisk={withAsterisk}
      disabled={disabled}
      readOnly={readOnly}
      placeholder={placeholder}
      value={national}
      onChange={(e) => handleText(e.currentTarget.value)}
      onBlur={onBlur}
      leftSectionWidth={92}
      leftSectionPointerEvents={disabled || readOnly ? 'none' : 'all'}
      leftSection={
        <Select
          aria-label={t('phone.countryCode', 'Country code')}
          data={countryOptions}
          value={country}
          onChange={handleCountryChange}
          renderOption={renderCountryOption}
          filter={filterCountries}
          searchable
          disabled={disabled || readOnly}
          variant="unstyled"
          size="xs"
          w={92}
          maxDropdownHeight={320}
          comboboxProps={{ width: 260, position: 'bottom-start' }}
          leftSection={<CountryFlag code={country} />}
          leftSectionWidth={30}
        />
      }
    />
  );
}
