/** Convert an absolute instant into weekday + minute-of-day in an IANA time zone. */

const JS_WEEKDAY: Record<string, number> = {
  Sun: 0,
  Mon: 1,
  Tue: 2,
  Wed: 3,
  Thu: 4,
  Fri: 5,
  Sat: 6,
};

export function isValidIanaTimeZone(timeZone: string): boolean {
  if (!timeZone || timeZone.length > 64) {
    return false;
  }
  try {
    Intl.DateTimeFormat('en-US', { timeZone }).format(new Date(0));
    return true;
  } catch {
    return false;
  }
}

export function zonedWeekdayAndMinutes(
  date: Date,
  timeZone: string,
): { jsWeekday: number; minuteOfDay: number } {
  const parts = new Intl.DateTimeFormat('en-US', {
    timeZone,
    weekday: 'short',
    hour: '2-digit',
    minute: '2-digit',
    hourCycle: 'h23',
  }).formatToParts(date);

  const weekdayToken = parts.find((p) => p.type === 'weekday')?.value ?? 'Sun';
  let hour = Number(parts.find((p) => p.type === 'hour')?.value ?? '0');
  const minute = Number(parts.find((p) => p.type === 'minute')?.value ?? '0');
  if (hour === 24) {
    hour = 0;
  }

  return {
    jsWeekday: JS_WEEKDAY[weekdayToken] ?? 0,
    minuteOfDay: hour * 60 + minute,
  };
}

/** Weekday of a YYYY-MM-DD civil date (Gregorian; same worldwide). */
export function civilDateJsWeekday(isoDate: string): number {
  const [y, m, d] = isoDate.split('-').map(Number);
  const utcNoon = new Date(Date.UTC(y, m - 1, d, 12, 0, 0, 0));
  return utcNoon.getUTCDay();
}

/**
 * Today's civil date (`YYYY-MM-DD`) in an IANA zone, so the server derives "today" from a
 * client-supplied *zone* rather than trusting a client-supplied date.
 */
export function civilDateInZone(date: Date, timeZone: string): string {
  // Intl throws RangeError on an unknown zone and this takes a client-supplied string;
  // callers validate first, this is the backstop.
  const zone = isValidIanaTimeZone(timeZone) ? timeZone : 'UTC';
  const parts = new Intl.DateTimeFormat('en-CA', {
    timeZone: zone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).formatToParts(date);

  const year = parts.find((p) => p.type === 'year')?.value ?? '1970';
  const month = parts.find((p) => p.type === 'month')?.value ?? '01';
  const day = parts.find((p) => p.type === 'day')?.value ?? '01';
  return `${year}-${month}-${day}`;
}
