import * as Localization from 'expo-localization';

/**
 * Immotech dial codes — longest first so "212" (MA) wins over "1" (US).
 * Never treat short Maghreb locals as NANP (+1).
 */
const COUNTRY_CODES = ['216', '212', '213', '971', '966', '33', '32', '1'] as const;

/** Regions that may set the default dial code from the device locale. */
const REGION_TO_CC: Record<string, string> = {
  TN: '216',
  MA: '212',
  DZ: '213',
  FR: '33',
  BE: '32',
  AE: '971',
  SA: '966',
  // US/CA deliberately omitted — phone language ≠ CRM market (Tunisia/Maghreb).
};

/** Expected national length after country code (without trunk 0). */
const NATIONAL_LEN: Record<string, number> = {
  '216': 8, // Tunisia
  '212': 9, // Morocco
  '213': 9, // Algeria
  '33': 9, // France
  '32': 8, // Belgium (varies)
  '971': 9,
  '966': 9,
  '1': 10, // NANP
};

/** CRM settings.country_code → dial code (set after form-options / sync). */
let crmDefaultCountryCode: string | null = null;

export function setCrmDefaultCountryCode(code: string | null | undefined): void {
  const digits = String(code ?? '').replace(/\D/g, '');
  crmDefaultCountryCode = digits.length >= 1 && digits.length <= 4 ? digits : null;
}

/** CRM dial code if known, else Immotech market region, else Tunisia. */
export function detectDefaultCountryCode(): string {
  if (crmDefaultCountryCode) return crmDefaultCountryCode;
  try {
    const region = Localization.getLocales()[0]?.regionCode?.toUpperCase();
    if (region && REGION_TO_CC[region]) return REGION_TO_CC[region];
  } catch {
    // ignore
  }
  return '216';
}

function nationalLen(country: string): number {
  return NATIONAL_LEN[country] ?? 8;
}

/** Strip national trunk prefix (leading 0s), e.g. 0612345678 → 612345678. */
function stripTrunkZero(local: string): string {
  return local.replace(/^0+/, '');
}

/**
 * Canonical E.164 digits (no +) for a known country, or null if invalid.
 * Accepts optional trunk 0 after the country code (2120… → 212…).
 */
function canonicalize(digits: string, country: string): string | null {
  if (!digits.startsWith(country)) return null;
  const local = stripTrunkZero(digits.slice(country.length));
  if (!local) return null;
  if (!validateLocal(local, country)) return null;
  return country + local;
}

function validateLocal(local: string, country: string): boolean {
  if (country === '1') {
    return local.length === 10;
  }
  if (country === '216') {
    return local.length === 8;
  }
  if (country === '212' || country === '213') {
    // Mobile usually 9; allow 8–9 for older / partial CRM rows
    return local.length >= 8 && local.length <= 9;
  }
  if (country === '33') {
    return local.length === 9;
  }
  const n = nationalLen(country);
  return local.length >= Math.max(7, n - 1) && local.length <= n + 1;
}

/** True when `digits` is a plausible full international number for `country`. */
function validateDigits(digits: string, country: string): boolean {
  return canonicalize(digits, country) != null;
}

/** Match an explicit country code prefix on full digit string (strict for +1). */
function matchCountryCode(digits: string): { cc: string; local: string; digits: string } | null {
  for (const cc of COUNTRY_CODES) {
    if (!digits.startsWith(cc) || digits.length <= cc.length) continue;
    const canon = canonicalize(digits, cc);
    if (canon) {
      return { cc, local: canon.slice(cc.length), digits: canon };
    }
  }
  return null;
}

/**
 * Guess Maghreb mobile when CRM default is wrong / not loaded yet.
 * Morocco & Algeria mobiles commonly start with 5/6/7 (9 digits).
 */
function guessMaghrebInternational(digits: string): string | null {
  if (/^[567]\d{8}$/.test(digits)) {
    return `212${digits}`;
  }
  if (/^0[567]\d{8}$/.test(digits)) {
    return `212${digits.slice(1)}`;
  }
  return null;
}

/** Attach default country to a national number (with or without leading 0). */
function withDefaultCountry(digits: string, defaultCountry: string): string | null {
  const nLen = nationalLen(defaultCountry);

  if (digits.startsWith('0') && digits.length === nLen + 1) {
    const local = stripTrunkZero(digits);
    const combined = defaultCountry + local;
    return canonicalize(combined, defaultCountry);
  }

  if (digits.length === nLen) {
    return canonicalize(defaultCountry + digits, defaultCountry);
  }

  // Tunisia still often typed as 8 digits even when CRM is Morocco — keep 8→default only when default is TN
  if (defaultCountry === '216' && digits.length === 8) {
    return canonicalize(`216${digits}`, '216');
  }

  return null;
}

export type PhoneSplit = {
  /** Digits only, e.g. "216" */
  prefix: string;
  /** National number without country code */
  number: string;
  /** Full E.164 digits without +, or null if invalid */
  digits: string | null;
  error: string | null;
};

/**
 * Split / validate like CRM `splitPhoneForStorage`.
 * Saves as gsm_prefix + gsm (e.g. 216 + 55123456 → displayed +216 55 123 456).
 */
/** Split a stored full number into prefix + national for form fields. */
export function parsePhoneToParts(
  raw: string,
  defaultCc = detectDefaultCountryCode(),
): { prefix: string; number: string } {
  const split = splitPhoneForStorage(`+${defaultCc}`, String(raw ?? ''), defaultCc);
  return { prefix: split.prefix || defaultCc, number: split.number };
}

export function splitPhoneForStorage(
  prefix: string,
  number: string,
  defaultCountry = detectDefaultCountryCode(),
): PhoneSplit {
  let pfxDigits = (prefix || '').replace(/\D/g, '') || defaultCountry;
  const numDigits = (number || '').replace(/\D/g, '');

  if (!numDigits) {
    return { prefix: pfxDigits, number: '', digits: null, error: null };
  }

  const matched = matchCountryCode(numDigits);
  if (matched) {
    return {
      prefix: matched.cc,
      number: matched.local,
      digits: matched.digits,
      error: null,
    };
  }

  if (pfxDigits && numDigits.startsWith(pfxDigits) && numDigits.length > pfxDigits.length) {
    const canon = canonicalize(numDigits, pfxDigits);
    if (canon) {
      return {
        prefix: pfxDigits,
        number: canon.slice(pfxDigits.length),
        digits: canon,
        error: null,
      };
    }
  }

  const local = stripTrunkZero(numDigits);
  const country = pfxDigits || defaultCountry;
  const canon = canonicalize(country + local, country);
  if (canon) {
    return {
      prefix: country,
      number: canon.slice(country.length),
      digits: canon,
      error: null,
    };
  }

  // National shape for default country (0 + local or bare local)
  const fromNational = withDefaultCountry(numDigits, defaultCountry);
  if (fromNational) {
    const m = matchCountryCode(fromNational);
    if (m) {
      return { prefix: m.cc, number: m.local, digits: m.digits, error: null };
    }
  }

  // Morocco-shaped mobile even if CRM default is still Tunisia
  const maghreb = guessMaghrebInternational(numDigits);
  if (maghreb) {
    const m = matchCountryCode(maghreb);
    if (m) {
      return { prefix: m.cc, number: m.local, digits: m.digits, error: null };
    }
  }

  if (country === '216' && local.length !== 8) {
    return {
      prefix: country,
      number: local,
      digits: null,
      error: 'phone.invalidTunisia',
    };
  }

  return {
    prefix: country,
    number: local,
    digits: null,
    error: 'phone.invalidFormat',
  };
}

/**
 * Normalize a number from the Android call log / dialer.
 * Preserves + / 00 international (Morocco +212…, France +33…).
 * Handles national forms with or without leading 0 (MA 0612… / 612…).
 */
export function normalizePhoneFromDevice(raw: string): string {
  const trimmed = String(raw ?? '').trim();
  if (!trimmed) return '';

  const defaultCountry = detectDefaultCountryCode();

  // Explicit international: +212… or 00…
  if (trimmed.startsWith('+') || trimmed.startsWith('00')) {
    let digits = trimmed.replace(/\D/g, '');
    if (trimmed.startsWith('00')) digits = digits.replace(/^00/, '');
    const matched = matchCountryCode(digits);
    if (matched) return matched.digits;
    // Keep digits as returned by the phone (full number from SIM/network)
    return stripTrunkZeroAfterKnownCc(digits);
  }

  const digits = trimmed.replace(/\D/g, '');
  if (!digits) return '';

  // Already a known international form from the log (incl. 2120… trunk kept)
  const matched = matchCountryCode(digits);
  if (matched) return matched.digits;

  const fromDefault = withDefaultCountry(digits, defaultCountry);
  if (fromDefault) return fromDefault;

  const maghreb = guessMaghrebInternational(digits);
  if (maghreb) return maghreb;

  // Longer unknown — keep as phone provided (do not invent +1)
  return digits;
}

/** If digits start with a known CC + trunk 0, drop the 0. */
function stripTrunkZeroAfterKnownCc(digits: string): string {
  for (const cc of COUNTRY_CODES) {
    if (digits.startsWith(cc + '0') && digits.length > cc.length + 1) {
      const canon = canonicalize(digits, cc);
      if (canon) return canon;
    }
  }
  return digits;
}

/** Digits for API / dial / WhatsApp (best-effort; may be incomplete). */
export function normalizePhoneInput(raw: string): string {
  return normalizePhoneFromDevice(raw);
}

/**
 * Alternate forms for CRM lookup (LIKE / exact), e.g. 2126…, 06…, 6…, last 8–9.
 * Order: most specific first.
 */
export function phoneSearchVariants(raw: string): string[] {
  const full = normalizePhoneFromDevice(raw);
  if (!full) return [];

  const out: string[] = [];
  const push = (v: string) => {
    const d = v.replace(/\D/g, '');
    if (d && !out.includes(d)) out.push(d);
  };

  push(full);
  const matched = matchCountryCode(full);
  if (matched) {
    push(matched.local);
    push(`0${matched.local}`);
    push(`${matched.cc}0${matched.local}`);
  } else {
    const local = stripTrunkZero(full);
    push(local);
    if (local && !local.startsWith('0')) push(`0${local}`);
  }

  if (full.length >= 9) push(full.slice(-9));
  if (full.length >= 8) push(full.slice(-8));

  return out;
}

/** Number exactly as the device gave it (dialer / call log) — never normalized. */
export function formatPhoneAsProvided(raw?: string | null): string {
  return String(raw ?? '').trim().replace(/\s+/g, ' ');
}

/** Number exactly as stored in the CRM (gsm_prefix + gsm), e.g. "+212 612345678". */
export function formatCrmStoredPhone(prefix?: string | null, number?: string | null): string {
  const num = String(number ?? '').trim();
  if (!num) return '';
  const pfx = String(prefix ?? '').trim();
  if (!pfx) return num;
  const pfxDigits = pfx.replace(/\D/g, '');
  if (!pfxDigits) return num;
  if (num.replace(/\D/g, '').startsWith(pfxDigits)) return `+${num.replace(/^\+/, '')}`;
  return `+${pfxDigits} ${num}`;
}

/** True when two numbers point to the same handset (ignores +, 0, country code). */
export function samePhone(a?: string | null, b?: string | null): boolean {
  const na = normalizePhoneFromDevice(String(a ?? ''));
  const nb = normalizePhoneFromDevice(String(b ?? ''));
  if (!na || !nb) return false;
  if (na === nb) return true;
  const tail = (d: string) => (d.length >= 8 ? d.slice(-8) : d);
  return tail(na) === tail(nb);
}

export function isValidPhone(prefix: string, number: string): boolean {
  return splitPhoneForStorage(prefix, number).digits != null;
}

/** True when a non-empty phone string is a complete valid number. */
export function isValidPhoneRaw(raw: string): boolean {
  const digits = String(raw ?? '').replace(/\D/g, '');
  if (!digits) return false;
  return splitPhoneForStorage('', raw).digits != null;
}

export function formatPhoneDisplay(raw: string): string {
  const digits = normalizePhoneFromDevice(raw) || String(raw ?? '').replace(/\D/g, '');
  if (!digits) return '';

  if (/^216\d{8}$/.test(digits)) {
    const local = digits.slice(3);
    return `+216 ${local.slice(0, 2)} ${local.slice(2, 5)} ${local.slice(5)}`.trim();
  }

  if (/^212\d{9}$/.test(digits)) {
    const local = digits.slice(3);
    return `+212 ${local.slice(0, 1)} ${local.slice(1, 5)} ${local.slice(5)}`.trim();
  }

  const matched = matchCountryCode(digits);
  if (matched) {
    return `+${matched.cc} ${matched.local}`.trim();
  }

  // Incomplete Tunisia (e.g. still typing)
  if (digits.startsWith('216') && digits.length < 11) {
    return `+216 ${digits.slice(3)}`.trim();
  }

  const cc = detectDefaultCountryCode();
  const nLen = nationalLen(cc);
  if (digits.length === nLen) {
    return `+${cc} ${digits}`.trim();
  }
  if (digits.startsWith('0') && digits.length === nLen + 1) {
    return `+${cc} ${digits.slice(1)}`.trim();
  }

  if (String(raw).trim().startsWith('+')) return String(raw).trim();
  // Do not force "+1…" on ambiguous digit strings
  return digits.length >= 10 ? `+${digits}` : digits;
}

export function toIsoLocal(d: Date): string {
  const pad = (n: number) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:00`;
}
