import { z } from 'zod';
import { AsYouType, Metadata, getCountryCallingCode, isValidPhoneNumber, parsePhoneNumberFromString, type CountryCode } from 'libphonenumber-js';

/**
 * Accepts any international number in E.164 (`+<country><number>`) or a
 * bare national number, which is assumed Ethiopian (`0911223344` ->
 * `+251911223344`) for backward compatibility with existing records.
 */
export const phoneNumber = z
  .string()
  .trim()
  .transform((v) => parsePhoneNumberFromString(v, 'ET')?.number ?? v)
  .superRefine((v, ctx) => {
    if (!v) ctx.addIssue({ code: 'custom', message: 'Phone number is required' });
    else if (!isValidPhoneNumber(v)) ctx.addIssue({ code: 'custom', message: 'Enter a valid phone number' });
  });

/** Same rules, but blank is allowed. */
export const optionalPhoneNumber = z.union([z.literal(''), phoneNumber]).optional();

/**
 * Digits the field should hold after an edit. A keystroke that only removed
 * a formatting character (backspacing the ')' out of "(415)") leaves the
 * digits unchanged, which would otherwise make the caret stick — drop a real
 * digit in that case.
 */
export function nextNationalDigits(text: string, prevDisplay: string): string {
  const digits = text.replace(/\D/g, '');
  const deleting = text.length < prevDisplay.length;
  if (deleting && digits === prevDisplay.replace(/\D/g, '')) return digits.slice(0, -1);
  return digits;
}

/** National digits of a stored value, for display in the number box. */
export function toNationalDigits(value: string, country: CountryCode): string {
  if (!value) return '';
  // `country` also resolves legacy national records ("0911223344").
  const parsed = parsePhoneNumberFromString(value, country);
  if (parsed) return parsed.nationalNumber;
  if (value.startsWith('+')) {
    const prefix = `+${getCountryCallingCode(country)}`;
    if (value.startsWith(prefix)) return value.slice(prefix.length).replace(/\D/g, '');
  }
  return value.replace(/\D/g, '');
}

/**
 * E.164 for the digits typed so far. Incomplete numbers don't parse, so they
 * fall back to a plain dial-code concatenation rather than collapsing to ''
 * — the value has to survive mid-typing for the field to be usable.
 */
export function toE164(digits: string, country: CountryCode): string {
  if (!digits) return '';
  return (
    parsePhoneNumberFromString(digits, country)?.number ??
    `+${getCountryCallingCode(country)}${digits}`
  );
}

/**
 * Digits grouped for display ("91 122 3344"). Formatted as an international
 * number with the dial code cut off: AsYouType's national mode leaves the
 * number ungrouped unless the trunk prefix was typed.
 */
export function formatNational(digits: string, country: CountryCode): string {
  if (!digits) return '';
  const prefix = `+${getCountryCallingCode(country)}`;
  const formatted = new AsYouType().input(prefix + digits);
  return formatted.startsWith(prefix) ? formatted.slice(prefix.length).trimStart() : digits;
}

const metadata = new Metadata();

/** Longest national number the country's numbering plan allows. */
export function maxNationalLength(country: CountryCode): number {
  metadata.selectNumberingPlan(country);
  return Math.max(...(metadata.numberingPlan?.possibleLengths() ?? [15]));
}

/**
 * True when the typed digits exceed the country's longest number. Judged on
 * the national part once it parses, so a trunk prefix (0911223344) or typed
 * country code (251911223344) isn't counted against the limit.
 */
export function exceedsMaxLength(digits: string, country: CountryCode): boolean {
  const national = parsePhoneNumberFromString(digits, country)?.nationalNumber ?? digits;
  return national.length > maxNationalLength(country);
}
