import React from 'react';
import { CountryFlag } from '@theorchard/suite-icons';
import {
    AsYouType,
    getCountryCallingCode,
    getExampleNumber,
    isSupportedCountry,
    parsePhoneNumberFromString,
    validatePhoneNumberLength,
} from 'libphonenumber-js/min';
import examples from 'libphonenumber-js/examples.mobile.json';
import { getCountries } from '../../utils/countries';
import type { PhoneCountryOption, PhoneCountryCode, PhoneInputValue } from './types';
import type { SelectOption } from '../select';

// Source the country list from suite-components' countries util (which respects
// suite-i18n's current locale via @theorchard/countries). libphonenumber-js
// provides the calling code for each. Any entry libphonenumber doesn't
// recognize (e.g. reserved territories) is dropped — keeps the list aligned
// with the rest of Suite's CountrySelect / MarketSelector dropdowns.
export const getPhoneCountryOptions = (locale?: string): PhoneCountryOption[] =>
    getCountries(locale ? { locale } : undefined)
        .filter((c) => isSupportedCountry(c.value as PhoneCountryCode))
        .map((c) => ({
            code: c.value as PhoneCountryCode,
            name: c.label,
            callingCode: getCountryCallingCode(c.value as PhoneCountryCode),
        }))
        .sort((a, b) => a.name.localeCompare(b.name, locale));

export const getPhoneCountrySelectOptions = (locale?: string): SelectOption[] =>
    getPhoneCountryOptions(locale).map((opt) => ({
        value: opt.code,
        label: `${opt.name} (+${opt.callingCode})`,
        icon: <CountryFlag countryCode={opt.code} size={16} />,
        // Searchable by country name or calling code only. The ISO alpha-2
        // code is deliberately NOT a keyword: as a `starts-with` match it
        // false-positives on any query beginning with those two letters
        // (e.g. "Alemania" would match Albania via its `AL` code).
        keywords: [
            { value: opt.name, match: 'includes' },
            { value: `+${opt.callingCode}`, match: 'starts-with' },
            { value: opt.callingCode, match: 'starts-with' },
        ],
    }));

// Reduce a value to digits plus at most one leading `+`. Any `+` that is not
// at position 0 is dropped, so the result is safe for `startsWith('+')` checks
// and for feeding into libphonenumber helpers.
export const stripFormatting = (value: string): string =>
    value.replace(/[^\d+]/g, '').replace(/(?!^)\+/g, '');

// Reduce a value to its national digits. Only an explicit international
// prefix (`+44 …`) gets the calling code stripped — a bare national string is
// returned untouched. Stripping a leading calling-code digit from national
// input would corrupt countries whose national numbers can start with that
// digit (e.g. Kazakhstan, calling code 7, national numbers starting with 7).
const toNationalDigits = (value: string, country: PhoneCountryCode): string => {
    const stripped = stripFormatting(value);
    if (!stripped.startsWith('+')) return stripped;
    const digits = stripped.slice(1);
    const cc = getCountryCallingCode(country);
    return digits.startsWith(cc) ? digits.slice(cc.length) : digits;
};

// `+1` countries (US, Canada, etc.) all share a fixed 10-digit national number.
const PLUS_ONE_MAX_DIGITS = 10;
const isPlusOneCountry = (country: PhoneCountryCode) => getCountryCallingCode(country) === '1';

export const isTooLong = (value: string, country: PhoneCountryCode): boolean => {
    // Empty input is never "too long" — let backspace-to-empty through.
    const digits = toNationalDigits(value, country);
    if (!digits) return false;
    // `+1` numbers are always 10 digits; cap by raw count since libphonenumber
    // reads a leading 1 as the country code and reports an 11-digit string as valid.
    if (isPlusOneCountry(country)) return digits.length > PLUS_ONE_MAX_DIGITS;
    // Otherwise only block past the country's longest valid length.
    // `INVALID_LENGTH` (a gap between two valid lengths, e.g. Canada's 7 and 10)
    // must NOT block — the user is mid-way to a longer valid number.
    return validatePhoneNumberLength(digits, country) === 'TOO_LONG';
};

// Truncate digits to the country's longest valid national length.
export const capToCountryMax = (digits: string, country: PhoneCountryCode): string => {
    if (isPlusOneCountry(country)) return digits.slice(0, PLUS_ONE_MAX_DIGITS);
    let truncated = digits;
    while (truncated && validatePhoneNumberLength(truncated, country) === 'TOO_LONG') {
        truncated = truncated.slice(0, -1);
    }
    return truncated;
};

// National-format the given value. `+1` countries get a manual `(NNN) NNN-NNNN` formatter because AsYouType misreads a leading 1 as the trunk prefix and emits `1 (NNN) NNN-NNN`. Other countries go through AsYouType, falling back to a parsed national format for countries with a trunk prefix (e.g. UK's leading 0) whose grouping AsYouType only applies once the number is complete and valid.
export const formatAsYouType = (value: string, country: PhoneCountryCode): string => {
    if (!value) return '';
    const digits = toNationalDigits(value, country);
    if (!digits) return '';
    if (isPlusOneCountry(country)) {
        const capped = digits.slice(0, PLUS_ONE_MAX_DIGITS);
        if (capped.length < 3) return `(${capped}`;
        if (capped.length === 3) return `(${capped})`;
        if (capped.length <= 6) return `(${capped.slice(0, 3)}) ${capped.slice(3)}`;
        return `(${capped.slice(0, 3)}) ${capped.slice(3, 6)}-${capped.slice(6)}`;
    }
    const formatted = new AsYouType(country).input(digits);
    if (formatted === digits) {
        const parsed = parsePhoneNumberFromString(digits, country);
        if (parsed?.country === country && parsed.isValid()) return parsed.formatNational();
    }
    return formatted;
};

export const parsePhoneInput = (value: string, country: PhoneCountryCode): PhoneInputValue => {
    const stripped = stripFormatting(value);
    const parsed = parsePhoneNumberFromString(stripped, country);
    const isValid = parsed?.isValid() ?? false;

    if (parsed?.country) {
        return {
            value: parsed.formatNational(),
            country: parsed.country,
            e164: isValid ? parsed.number : undefined,
            isValid,
        };
    }

    return {
        value,
        country,
        isValid: false,
    };
};

// National-format placeholder that's obviously a placeholder: takes the country's example number from libphonenumber, keeps its grouping/punctuation, and replaces every digit with a sequential one (1-9-0-1-2...). US -> `(123) 456-7890`, ES -> `123 45 67 89`.
export const getExamplePlaceholder = (country: PhoneCountryCode): string | undefined => {
    const formatted = getExampleNumber(country, examples)?.formatNational();
    if (!formatted) return undefined;
    let i = 0;
    return formatted.replace(/\d/g, () => String(++i % 10));
};

// Returns the country libphonenumber can identify from a value's international prefix (`+44 …` → `'GB'`). Returns undefined for partial / national-only inputs.
export const detectCountryFromInternational = (value: string): PhoneCountryCode | undefined => {
    const stripped = stripFormatting(value);
    if (!stripped.startsWith('+')) return undefined;
    const parsed = parsePhoneNumberFromString(stripped);
    return parsed?.country;
};
