/**
 * Fleet scheduling — maintenance window management for Token.
 *
 * Allows users to schedule planned downtime windows for devices so the alert
 * system can suppress false-positive offline notifications during maintenance.
 *
 * Supports one-shot and recurring (daily/weekly) windows.
 */

import { db } from "@/lib/db";
import { logger } from "@/lib/logger";

export type RecurringType = "none" | "daily" | "weekly";

/* ── Helpers ──────────────────────────────────────────────────────────── */

/**
 * Check whether `now` falls within a recurring window.
 *
 * For daily: matches if now's time-of-day is within [startTime, endTime] time-of-day.
 * For weekly: matches if now is the same day-of-week AND within time-of-day.
 *
 * Edge case: windows that span midnight are handled by checking if nowMinutes
 * wraps around (start > end means overnight window).
 */
function matchesRecurring(
  startTime: Date,
  endTime: Date,
  recurring: string,
  now: Date,
): boolean {
  if (recurring === "none") return false;

  const startMinutes = startTime.getUTCHours() * 60 + startTime.getUTCMinutes();
  const endMinutes = endTime.getUTCHours() * 60 + endTime.getUTCMinutes();
  const nowMinutes = now.getUTCHours() * 60 + now.getUTCMinutes();

  if (recurring === "weekly") {
    // Must be the same day-of-week as the original startTime
    if (now.getUTCDay() !== startTime.getUTCDay()) return false;
  }

  // Handle overnight windows (e.g., 23:00 - 02:00)
  if (startMinutes > endMinutes) {
    return nowMinutes >= startMinutes || nowMinutes <= endMinutes;
  }

  return nowMinutes >= startMinutes && nowMinutes <= endMinutes;
}

/* ── Public API ───────────────────────────────────────────────────────── */

/**
 * Check if a device currently has an active maintenance window.
 *
 * Returns true if `now` falls within any of the device's maintenance windows,
 * accounting for one-shot and recurring (daily/weekly) schedules.
 */
export async function isInMaintenanceWindow(deviceId: string): Promise<boolean> {
  const now = new Date();

  // Fetch all windows for this device that haven't definitively ended
  // (for non-recurring, endTime must be in the future; for recurring, we
  //  fetch all and check time-of-day in code)
  const windows = await db.maintenanceWindow.findMany({
    where: {
      deviceId,
      OR: [
        // One-shot: now must be between start and end
        { recurring: "none", startTime: { lte: now }, endTime: { gte: now } },
        // Recurring: fetch all recurring windows (filter in code)
        { recurring: { not: "none" } },
      ],
    },
  });

  for (const w of windows) {
    if (w.recurring === "none") {
      // One-shot window: the DB query already filtered it; it's active
      return true;
    }

    // Recurring window: check time-of-day (and day-of-week for weekly)
    if (matchesRecurring(w.startTime, w.endTime, w.recurring, now)) {
      logger.debug(
        {
          action: "maintenance_window_active",
          device_id: deviceId,
          window_id: w.id,
          recurring: w.recurring,
        },
        "Device is in recurring maintenance window",
      );
      return true;
    }
  }

  return false;
}

/**
 * Get all upcoming maintenance windows for a user's devices.
 *
 * Returns windows whose endTime is in the future (for non-recurring) or
 * all recurring windows, ordered by startTime ascending.
 */
export async function getUpcomingWindows(userId: string) {
  const now = new Date();

  return db.maintenanceWindow.findMany({
    where: {
      userId,
      OR: [
        { recurring: "none", endTime: { gt: now } },
        { recurring: { not: "none" } },
      ],
    },
    orderBy: { startTime: "asc" },
  });
}

/**
 * Get only currently active maintenance windows for a user.
 *
 * A window is "active" if now is between startTime and endTime (one-shot)
 * or if now matches the recurring pattern.
 */
export async function getActiveWindows(userId: string) {
  const now = new Date();

  const candidates = await db.maintenanceWindow.findMany({
    where: {
      userId,
      OR: [
        { recurring: "none", startTime: { lte: now }, endTime: { gte: now } },
        { recurring: { not: "none" } },
      ],
    },
    orderBy: { startTime: "asc" },
  });

  // Filter recurring windows to only those currently active
  return candidates.filter((w) => {
    if (w.recurring === "none") return true; // already filtered by DB
    return matchesRecurring(w.startTime, w.endTime, w.recurring, now);
  });
}

/* ── Exported for testing ─────────────────────────────────────────────── */

export { matchesRecurring as _matchesRecurring };
