/**
 * Utilities for overnight pause windows.
 * Works in both Node.js and browser — uses only Intl, no extra dependencies.
 */

function getLocalMinutes(timezone: string, now: Date): number {
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone: timezone,
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  });
  const parts = formatter.formatToParts(now);
  const hour = parseInt(parts.find((p) => p.type === "hour")?.value ?? "0", 10);
  const minute = parseInt(parts.find((p) => p.type === "minute")?.value ?? "0", 10);
  // Intl hour12:false can return "24" for midnight — normalize to 0
  return (hour === 24 ? 0 : hour) * 60 + minute;
}

/**
 * Build a UTC Date representing "the given date at HH:MM in the given timezone."
 * Corrects for the UTC offset by comparing what local time the naive UTC produces.
 */
function utcForLocalTime(
  timezone: string,
  year: number,
  month: number, // 1-indexed
  day: number,
  hour: number,
  minute: number
): Date {
  // Start with a naive UTC (treating target H:M as UTC)
  const approxUTC = Date.UTC(year, month - 1, day, hour, minute, 0);
  const probe = new Date(approxUTC);
  const localMins = getLocalMinutes(timezone, probe);
  const targetMins = hour * 60 + minute;
  // Shift: if local is behind target, add the gap; if ahead, subtract it
  return new Date(approxUTC + (targetMins - localMins) * 60 * 1000);
}

/**
 * Returns true if `now` falls inside the overnight pause window defined by
 * startHHMM–endHHMM in the given IANA timezone.
 *
 * The window almost always spans midnight (e.g. "23:00"–"07:00"), but a
 * same-day window (e.g. "10:00"–"18:00") is also handled correctly.
 */
export function isInOvernightWindow(
  timezone: string,
  startHHMM: string,
  endHHMM: string,
  now = new Date()
): boolean {
  const currentMinutes = getLocalMinutes(timezone, now);
  const [sh, sm] = startHHMM.split(":").map(Number);
  const [eh, em] = endHHMM.split(":").map(Number);
  const startMinutes = sh * 60 + sm;
  const endMinutes = eh * 60 + em;

  if (startMinutes > endMinutes) {
    // Spans midnight: in window if current >= start OR current < end
    return currentMinutes >= startMinutes || currentMinutes < endMinutes;
  }
  // Same-day window: in window if start <= current < end
  return currentMinutes >= startMinutes && currentMinutes < endMinutes;
}

/**
 * Returns the next UTC Date when endHHMM will occur in the given timezone.
 * Used to tell clients when the overnight window will end.
 */
export function getOvernightResumeUTC(
  timezone: string,
  endHHMM: string,
  now = new Date()
): Date {
  const [eh, em] = endHHMM.split(":").map(Number);

  // Get current calendar date in the target timezone
  const dateFmt = new Intl.DateTimeFormat("en-CA", {
    timeZone: timezone,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  });
  const [year, month, day] = dateFmt.format(now).split("-").map(Number);

  // Candidate: today at endHHMM in the target timezone
  const candidate = utcForLocalTime(timezone, year, month, day, eh, em);

  // If this moment has already passed, advance one day
  if (candidate.getTime() <= now.getTime()) {
    return new Date(candidate.getTime() + 24 * 60 * 60 * 1000);
  }
  return candidate;
}
