/**
 * Schedule Utilities for Backup
 *
 * Timezone-aware calculation of next backup times.
 * Used by both the backup worker and the settings API route
 * so that the user's chosen timezone (e.g. Asia/Kolkata for IST,
 * Asia/Dubai for GST) is respected.
 */

/**
 * Convert a desired local time (HH:mm) in the given IANA timezone
 * to the next occurrence of that time as a UTC Date.
 *
 * Uses a robust two-step approach:
 * 1. Figure out what the current date/time is in the target timezone.
 * 2. Build the target time string (YYYY-MM-DD HH:mm) in that timezone
 *    and use Date.parse with a timezone offset to get the exact UTC moment.
 *
 * This avoids all the pitfalls of DST transitions, date-boundary
 * crossings, and server-local-time ambiguity.
 */
export function nextOccurrenceInTimezone(
  hours: number,
  minutes: number,
  timezone: string,
  referenceDate: Date = new Date(),
): Date {
  try {
    // Step 1: Get current date/time parts in the target timezone
    const parts = new Intl.DateTimeFormat('en-US', {
      timeZone: timezone,
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      hour12: false,
    }).formatToParts(referenceDate);

    const p = (type: string) => parts.find(x => x.type === type)?.value ?? '';
    const year = parseInt(p('year'), 10);
    const month = parseInt(p('month'), 10);   // 1-12
    const day = parseInt(p('day'), 10);         // 1-31
    const currentHour = parseInt(p('hour'), 10);
    const currentMinute = parseInt(p('minute'), 10);

    // Step 2: Build a target Date in the target timezone.
    // We use Intl to compute the offset between UTC and the target tz
    // at the target moment, then apply that offset to get UTC.

    // Try today first
    let targetDate = buildUTCDate(year, month, day, hours, minutes, timezone);

    // If the target time has already passed today in the target tz, use tomorrow
    if (targetDate <= referenceDate) {
      // Advance day by day until we get a future date
      // (handles DST transitions that might skip/repeat hours)
      let nextDay = new Date(targetDate);
      nextDay.setUTCDate(nextDay.getUTCDate() + 1);
      // Recompute for the next day to handle DST correctly
      const nextDayParts = new Intl.DateTimeFormat('en-US', {
        timeZone: timezone,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
      }).formatToParts(nextDay);

      const np = (type: string) => nextDayParts.find(x => x.type === type)?.value ?? '';
      const ny = parseInt(np('year'), 10);
      const nm = parseInt(np('month'), 10);
      const nd = parseInt(np('day'), 10);

      targetDate = buildUTCDate(ny, nm, nd, hours, minutes, timezone);
    }

    return targetDate;
  } catch {
    // Fallback: treat as UTC
    const fallback = new Date(referenceDate);
    fallback.setUTCHours(hours, minutes, 0, 0);
    if (fallback <= referenceDate) {
      fallback.setUTCDate(fallback.getUTCDate() + 1);
    }
    return fallback;
  }
}

/**
 * Build a UTC Date representing "YYYY-MM-DD HH:mm in the given timezone".
 *
 * Uses Intl.DateTimeFormat to format the target date in the target timezone,
 * then parses the resulting string back to UTC. This avoids all reliance on
 * the server's local timezone, which was causing incorrect results when the
 * server timezone differed from UTC.
 */
function buildUTCDate(
  year: number,
  month: number,  // 1-12
  day: number,
  hours: number,
  minutes: number,
  timezone: string,
): Date {
  // Build a target ISO-like string in the target timezone using a known UTC reference.
  // Strategy: Take a UTC timestamp, format it in the target timezone, then
  // compute the offset between what the target timezone says and the UTC timestamp.
  // Then apply that offset to our target wall-clock time.

  // Use noon UTC on the given day as a reference point
  const noonUTC = Date.UTC(year, month - 1, day, 12, 0, 0);

  // Format noon UTC in the target timezone to get the wall-clock time there
  const noonParts = new Intl.DateTimeFormat('en-US', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).formatToParts(noonUTC);

  const np = (type: string) => noonParts.find(x => x.type === type)?.value ?? '';
  const tzYear = parseInt(np('year'), 10);
  const tzMonth = parseInt(np('month'), 10);
  const tzDay = parseInt(np('day'), 10);
  const tzHour = parseInt(np('hour'), 10);
  const tzMinute = parseInt(np('minute'), 10);

  // Compute the offset between the target timezone and UTC at noon on that day.
  // We use Date.UTC to create a pure UTC timestamp for the target timezone's wall-clock time.
  // This avoids any server-local-timezone dependency.
  const tzNoonAsUTC = Date.UTC(tzYear, tzMonth - 1, tzDay, tzHour, tzMinute, 0, 0);
  const offsetMs = tzNoonAsUTC - noonUTC;

  // Now compute our target wall-clock time as a pure UTC timestamp (as if it were UTC),
  // then subtract the offset to get the true UTC moment.
  const targetWallClockAsUTC = Date.UTC(year, month - 1, day, hours, minutes, 0, 0);
  return new Date(targetWallClockAsUTC - offsetMs);
}

/**
 * Calculate the next backup time based on settings.
 * Respects the user's configured timezone so that "1:12 PM IST"
 * actually means 1:12 PM in Asia/Kolkata, not the server's local time.
 *
 * Returns a UTC Date for when the next backup should occur.
 */
export function calculateNextBackupTime(setting: {
  time?: string;
  timezone?: string;
  frequency?: string;
  customIntervalDays?: number;
}): Date {
  const [hours, minutes] = (setting.time || '02:00').split(':').map(Number);
  const timezone = setting.timezone || 'UTC';
  const now = new Date();

  // Get the next occurrence of HH:MM in the user's timezone as a UTC Date
  const targetUTC = nextOccurrenceInTimezone(hours, minutes, timezone, now);

  // If the target time has already passed (shouldn't happen with nextOccurrenceInTimezone,
  // but safety check), advance by frequency period
  if (targetUTC <= now) {
    switch (setting.frequency) {
      case 'weekly':
        targetUTC.setUTCDate(targetUTC.getUTCDate() + 7);
        break;
      case 'monthly':
        targetUTC.setUTCMonth(targetUTC.getUTCMonth() + 1);
        break;
      case 'yearly':
        targetUTC.setUTCFullYear(targetUTC.getUTCFullYear() + 1);
        break;
      case 'custom':
        targetUTC.setUTCDate(targetUTC.getUTCDate() + (setting.customIntervalDays || 1));
        break;
      default: // daily
        targetUTC.setUTCDate(targetUTC.getUTCDate() + 1);
        break;
    }
  }

  console.log(
    `[scheduleUtils] calculateNextBackupTime: time=${setting.time || '02:00'}, tz=${timezone}, ` +
    `freq=${setting.frequency || 'daily'} => nextBackupAt=${targetUTC.toISOString()}`
  );

  return targetUTC;
}