/**
 * Country → IANA timezone mapping.
 *
 * For single-zone countries, value is one IANA string.
 * For multi-zone countries, value is the primary IANA string followed
 * by all valid alternates. Source: IANA tzdb (current as of 2026-04).
 *
 * The full ~600-entry IANA list comes from `Intl.supportedValuesOf('timeZone')`
 * at runtime; this map only handles the country→default lookup.
 */

import type { CountryCode } from './countries';

type TimezoneList = readonly [primary: string, ...alternates: string[]];

// Multi-zone countries - list every IANA zone.
const MULTI_ZONE: Partial<Record<CountryCode, TimezoneList>> = {
  AU: [
    'Australia/Sydney',
    'Australia/Melbourne',
    'Australia/Brisbane',
    'Australia/Adelaide',
    'Australia/Perth',
    'Australia/Hobart',
    'Australia/Darwin',
  ],
  BR: [
    'America/Sao_Paulo',
    'America/Manaus',
    'America/Rio_Branco',
    'America/Belem',
    'America/Recife',
    'America/Cuiaba',
    'America/Fortaleza',
    'America/Bahia',
    'America/Noronha',
  ],
  CA: [
    'America/Toronto',
    'America/Vancouver',
    'America/Edmonton',
    'America/Winnipeg',
    'America/Halifax',
    'America/St_Johns',
    'America/Regina',
    'America/Whitehorse',
  ],
  CD: ['Africa/Kinshasa', 'Africa/Lubumbashi'],
  ID: ['Asia/Jakarta', 'Asia/Pontianak', 'Asia/Makassar', 'Asia/Jayapura'],
  KZ: [
    'Asia/Almaty',
    'Asia/Atyrau',
    'Asia/Aqtau',
    'Asia/Aqtobe',
    'Asia/Oral',
    'Asia/Qostanay',
    'Asia/Qyzylorda',
  ],
  MN: ['Asia/Ulaanbaatar', 'Asia/Hovd', 'Asia/Choibalsan'],
  MX: [
    'America/Mexico_City',
    'America/Cancun',
    'America/Merida',
    'America/Monterrey',
    'America/Mazatlan',
    'America/Chihuahua',
    'America/Hermosillo',
    'America/Tijuana',
  ],
  RU: [
    'Europe/Moscow',
    'Europe/Kaliningrad',
    'Europe/Samara',
    'Asia/Yekaterinburg',
    'Asia/Omsk',
    'Asia/Novosibirsk',
    'Asia/Krasnoyarsk',
    'Asia/Irkutsk',
    'Asia/Yakutsk',
    'Asia/Vladivostok',
    'Asia/Magadan',
    'Asia/Kamchatka',
  ],
  US: [
    'America/New_York',
    'America/Chicago',
    'America/Denver',
    'America/Los_Angeles',
    'America/Phoenix',
    'America/Anchorage',
    'America/Honolulu',
    'America/Indianapolis',
    'America/Detroit',
    'America/Boise',
    'America/Adak',
    'America/Juneau',
    'America/Nome',
    'Pacific/Guam',
    'America/Puerto_Rico',
  ],
};

// Single-zone primary lookup. Sourced from IANA tzdb's zone1970.tab.
const SINGLE_ZONE: Partial<Record<CountryCode, string>> = {
  AD: 'Europe/Andorra',
  AE: 'Asia/Dubai',
  AF: 'Asia/Kabul',
  AG: 'America/Antigua',
  AI: 'America/Anguilla',
  AL: 'Europe/Tirane',
  AM: 'Asia/Yerevan',
  AO: 'Africa/Luanda',
  AQ: 'Antarctica/McMurdo',
  AR: 'America/Argentina/Buenos_Aires',
  AS: 'Pacific/Pago_Pago',
  AT: 'Europe/Vienna',
  AW: 'America/Aruba',
  AX: 'Europe/Mariehamn',
  AZ: 'Asia/Baku',
  BA: 'Europe/Sarajevo',
  BB: 'America/Barbados',
  BD: 'Asia/Dhaka',
  BE: 'Europe/Brussels',
  BF: 'Africa/Ouagadougou',
  BG: 'Europe/Sofia',
  BH: 'Asia/Bahrain',
  BI: 'Africa/Bujumbura',
  BJ: 'Africa/Porto-Novo',
  BL: 'America/St_Barthelemy',
  BM: 'Atlantic/Bermuda',
  BN: 'Asia/Brunei',
  BO: 'America/La_Paz',
  BQ: 'America/Kralendijk',
  BS: 'America/Nassau',
  BT: 'Asia/Thimphu',
  BV: 'Antarctica/Syowa',
  BW: 'Africa/Gaborone',
  BY: 'Europe/Minsk',
  BZ: 'America/Belize',
  CC: 'Indian/Cocos',
  CF: 'Africa/Bangui',
  CG: 'Africa/Brazzaville',
  CH: 'Europe/Zurich',
  CI: 'Africa/Abidjan',
  CK: 'Pacific/Rarotonga',
  CL: 'America/Santiago',
  CM: 'Africa/Douala',
  CN: 'Asia/Shanghai',
  CO: 'America/Bogota',
  CR: 'America/Costa_Rica',
  CU: 'America/Havana',
  CV: 'Atlantic/Cape_Verde',
  CW: 'America/Curacao',
  CX: 'Indian/Christmas',
  CY: 'Asia/Nicosia',
  CZ: 'Europe/Prague',
  DE: 'Europe/Berlin',
  DJ: 'Africa/Djibouti',
  DK: 'Europe/Copenhagen',
  DM: 'America/Dominica',
  DO: 'America/Santo_Domingo',
  DZ: 'Africa/Algiers',
  EC: 'America/Guayaquil',
  EE: 'Europe/Tallinn',
  EG: 'Africa/Cairo',
  EH: 'Africa/El_Aaiun',
  ER: 'Africa/Asmara',
  ES: 'Europe/Madrid',
  ET: 'Africa/Addis_Ababa',
  FI: 'Europe/Helsinki',
  FJ: 'Pacific/Fiji',
  FK: 'Atlantic/Stanley',
  FM: 'Pacific/Pohnpei',
  FO: 'Atlantic/Faroe',
  FR: 'Europe/Paris',
  GA: 'Africa/Libreville',
  GB: 'Europe/London',
  GD: 'America/Grenada',
  GE: 'Asia/Tbilisi',
  GF: 'America/Cayenne',
  GG: 'Europe/Guernsey',
  GH: 'Africa/Accra',
  GI: 'Europe/Gibraltar',
  GL: 'America/Godthab',
  GM: 'Africa/Banjul',
  GN: 'Africa/Conakry',
  GP: 'America/Guadeloupe',
  GQ: 'Africa/Malabo',
  GR: 'Europe/Athens',
  GS: 'Atlantic/South_Georgia',
  GT: 'America/Guatemala',
  GU: 'Pacific/Guam',
  GW: 'Africa/Bissau',
  GY: 'America/Guyana',
  HK: 'Asia/Hong_Kong',
  HM: 'Antarctica/Mawson',
  HN: 'America/Tegucigalpa',
  HR: 'Europe/Zagreb',
  HT: 'America/Port-au-Prince',
  HU: 'Europe/Budapest',
  IE: 'Europe/Dublin',
  IL: 'Asia/Jerusalem',
  IM: 'Europe/Isle_of_Man',
  IN: 'Asia/Kolkata',
  IO: 'Indian/Chagos',
  IQ: 'Asia/Baghdad',
  IR: 'Asia/Tehran',
  IS: 'Atlantic/Reykjavik',
  IT: 'Europe/Rome',
  JE: 'Europe/Jersey',
  JM: 'America/Jamaica',
  JO: 'Asia/Amman',
  JP: 'Asia/Tokyo',
  KE: 'Africa/Nairobi',
  KG: 'Asia/Bishkek',
  KH: 'Asia/Phnom_Penh',
  KI: 'Pacific/Tarawa',
  KM: 'Indian/Comoro',
  KN: 'America/St_Kitts',
  KP: 'Asia/Pyongyang',
  KR: 'Asia/Seoul',
  KW: 'Asia/Kuwait',
  KY: 'America/Cayman',
  LA: 'Asia/Vientiane',
  LB: 'Asia/Beirut',
  LC: 'America/St_Lucia',
  LI: 'Europe/Vaduz',
  LK: 'Asia/Colombo',
  LR: 'Africa/Monrovia',
  LS: 'Africa/Maseru',
  LT: 'Europe/Vilnius',
  LU: 'Europe/Luxembourg',
  LV: 'Europe/Riga',
  LY: 'Africa/Tripoli',
  MA: 'Africa/Casablanca',
  MC: 'Europe/Monaco',
  MD: 'Europe/Chisinau',
  ME: 'Europe/Podgorica',
  MF: 'America/Marigot',
  MG: 'Indian/Antananarivo',
  MH: 'Pacific/Majuro',
  MK: 'Europe/Skopje',
  ML: 'Africa/Bamako',
  MM: 'Asia/Yangon',
  MO: 'Asia/Macau',
  MP: 'Pacific/Saipan',
  MQ: 'America/Martinique',
  MR: 'Africa/Nouakchott',
  MS: 'America/Montserrat',
  MT: 'Europe/Malta',
  MU: 'Indian/Mauritius',
  MV: 'Indian/Maldives',
  MW: 'Africa/Blantyre',
  MY: 'Asia/Kuala_Lumpur',
  MZ: 'Africa/Maputo',
  NA: 'Africa/Windhoek',
  NC: 'Pacific/Noumea',
  NE: 'Africa/Niamey',
  NF: 'Pacific/Norfolk',
  NG: 'Africa/Lagos',
  NI: 'America/Managua',
  NL: 'Europe/Amsterdam',
  NO: 'Europe/Oslo',
  NP: 'Asia/Kathmandu',
  NR: 'Pacific/Nauru',
  NU: 'Pacific/Niue',
  NZ: 'Pacific/Auckland',
  OM: 'Asia/Muscat',
  PA: 'America/Panama',
  PE: 'America/Lima',
  PF: 'Pacific/Tahiti',
  PG: 'Pacific/Port_Moresby',
  PH: 'Asia/Manila',
  PK: 'Asia/Karachi',
  PL: 'Europe/Warsaw',
  PM: 'America/Miquelon',
  PN: 'Pacific/Pitcairn',
  PR: 'America/Puerto_Rico',
  PS: 'Asia/Gaza',
  PT: 'Europe/Lisbon',
  PW: 'Pacific/Palau',
  PY: 'America/Asuncion',
  QA: 'Asia/Qatar',
  RE: 'Indian/Reunion',
  RO: 'Europe/Bucharest',
  RS: 'Europe/Belgrade',
  RW: 'Africa/Kigali',
  SA: 'Asia/Riyadh',
  SB: 'Pacific/Guadalcanal',
  SC: 'Indian/Mahe',
  SD: 'Africa/Khartoum',
  SE: 'Europe/Stockholm',
  SG: 'Asia/Singapore',
  SH: 'Atlantic/St_Helena',
  SI: 'Europe/Ljubljana',
  SJ: 'Arctic/Longyearbyen',
  SK: 'Europe/Bratislava',
  SL: 'Africa/Freetown',
  SM: 'Europe/San_Marino',
  SN: 'Africa/Dakar',
  SO: 'Africa/Mogadishu',
  SR: 'America/Paramaribo',
  SS: 'Africa/Juba',
  ST: 'Africa/Sao_Tome',
  SV: 'America/El_Salvador',
  SX: 'America/Lower_Princes',
  SY: 'Asia/Damascus',
  SZ: 'Africa/Mbabane',
  TC: 'America/Grand_Turk',
  TD: 'Africa/Ndjamena',
  TF: 'Indian/Kerguelen',
  TG: 'Africa/Lome',
  TH: 'Asia/Bangkok',
  TJ: 'Asia/Dushanbe',
  TK: 'Pacific/Fakaofo',
  TL: 'Asia/Dili',
  TM: 'Asia/Ashgabat',
  TN: 'Africa/Tunis',
  TO: 'Pacific/Tongatapu',
  TR: 'Europe/Istanbul',
  TT: 'America/Port_of_Spain',
  TV: 'Pacific/Funafuti',
  TW: 'Asia/Taipei',
  TZ: 'Africa/Dar_es_Salaam',
  UA: 'Europe/Kyiv',
  UG: 'Africa/Kampala',
  UM: 'Pacific/Wake',
  UY: 'America/Montevideo',
  UZ: 'Asia/Tashkent',
  VA: 'Europe/Vatican',
  VC: 'America/St_Vincent',
  VE: 'America/Caracas',
  VG: 'America/Tortola',
  VI: 'America/St_Thomas',
  VN: 'Asia/Ho_Chi_Minh',
  VU: 'Pacific/Efate',
  WF: 'Pacific/Wallis',
  WS: 'Pacific/Apia',
  YE: 'Asia/Aden',
  YT: 'Indian/Mayotte',
  ZA: 'Africa/Johannesburg',
  ZM: 'Africa/Lusaka',
  ZW: 'Africa/Harare',
};

/**
 * Returns the IANA zone(s) for a country. Always returns at least one
 * entry; the first entry is the primary/most-populous zone.
 */
export function timezonesForCountry(country: CountryCode): readonly string[] {
  const multi = MULTI_ZONE[country];
  if (multi) return multi;
  const single = SINGLE_ZONE[country];
  return single ? [single] : [];
}

/**
 * Returns the single best-default IANA timezone for a country, or null
 * when the dataset has no entry (caller should fall back to a generic
 * default like 'UTC').
 */
export function primaryTimezoneFor(country: CountryCode): string | null {
  const list = timezonesForCountry(country);
  return list[0] ?? null;
}

/** True when the country has more than one valid zone (UI shows a sub-select). */
export function isMultiZone(country: CountryCode): boolean {
  const list = timezonesForCountry(country);
  return list.length > 1;
}

/**
 * Master IANA timezone list - uses Intl when available (modern browsers
 * + Node 21+). Falls back to a small bundled list when missing.
 */
export function listAllTimezones(): readonly string[] {
  if (typeof Intl !== 'undefined' && 'supportedValuesOf' in Intl) {
    try {
      const supported = Intl.supportedValuesOf('timeZone') as string[];
      if (Array.isArray(supported) && supported.length > 0) return supported;
    } catch {
      // fall through
    }
  }
  // Tiny fallback drawn from our country map - covers ~250 entries and
  // never less than the timezones we'd otherwise reference.
  const set = new Set<string>();
  for (const tz of Object.values(SINGLE_ZONE)) set.add(tz!);
  for (const list of Object.values(MULTI_ZONE)) {
    for (const tz of list ?? []) set.add(tz);
  }
  return Array.from(set).sort();
}

/**
 * Pretty-format a timezone for display: `'Europe/London (UTC+1)'`.
 * The offset is computed against `now` so it follows DST.
 */
export function formatTimezoneLabel(tz: string, now: Date = new Date()): string {
  try {
    const parts = new Intl.DateTimeFormat('en', {
      timeZone: tz,
      timeZoneName: 'shortOffset',
    }).formatToParts(now);
    const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '';
    return offset ? `${tz} (${offset})` : tz;
  } catch {
    return tz;
  }
}
