/**
 * Branch-local quiet hours gate. Each branch row carries `quiet_hours_enabled`,
 * `quiet_hours_start`, and `quiet_hours_end` (local hour, 0-23) — when not
 * set, defaults to 22:00-08:00. Resolved per-call via Intl.DateTimeFormat so
 * DST/timezone changes are picked up automatically.
 */

interface BranchHoursLite {
  timezone?: string | null;
  quiet_hours_enabled?: boolean | null;
  quiet_hours_start?: number | null;
  quiet_hours_end?: number | null;
}

const DEFAULT_QUIET_START = 22; // 10 PM local
const DEFAULT_QUIET_END = 8;    // 8 AM local

function clampHour(n: unknown, fallback: number): number {
  const v = Number(n);
  if (!Number.isFinite(v)) return fallback;
  const i = Math.floor(v);
  if (i < 0) return 0;
  if (i > 23) return 23;
  return i;
}

function resolveWindow(branch: BranchHoursLite | null | undefined): { start: number; end: number; enabled: boolean } {
  const enabled = branch?.quiet_hours_enabled === false ? false : true;
  const start = clampHour(branch?.quiet_hours_start, DEFAULT_QUIET_START);
  const end   = clampHour(branch?.quiet_hours_end,   DEFAULT_QUIET_END);
  return { start, end, enabled };
}

function localHour(tz: string, when: Date): number {
  try {
    const fmt = new Intl.DateTimeFormat('en-US', { hour: 'numeric', hour12: false, timeZone: tz });
    const parts = fmt.formatToParts(when);
    const hour = parts.find((p) => p.type === 'hour')?.value;
    const n = parseInt(hour ?? '', 10);
    return Number.isFinite(n) ? n : when.getUTCHours();
  } catch {
    return when.getUTCHours();
  }
}

export function isWithinQuietHours(
  branch: BranchHoursLite | null | undefined,
  when: Date = new Date(),
): boolean {
  const { start, end, enabled } = resolveWindow(branch);
  if (!enabled) return false;
  if (start === end) return false; // Degenerate window — treat as disabled.
  const tz = branch?.timezone || 'UTC';
  const h = localHour(tz, when);
  if (start < end) {
    // Same-day window e.g. 1..5 — quiet between 1AM and 5AM.
    return h >= start && h < end;
  }
  // Wraps midnight (default case): 22..23 OR 0..7
  return h >= start || h < end;
}

/** Compute the next time delivery is allowed. Returns the input when not blocked. */
export function nextAllowedAt(
  branch: BranchHoursLite | null | undefined,
  when: Date = new Date(),
): Date {
  if (!isWithinQuietHours(branch, when)) return when;
  // Push to the end of the quiet window in 30-minute steps; cheap (≤ 48
  // iterations even for a maximally-wide 23h window).
  const cursor = new Date(when.getTime());
  for (let i = 0; i < 48; i++) {
    cursor.setUTCMinutes(cursor.getUTCMinutes() + 30);
    if (!isWithinQuietHours(branch, cursor)) return cursor;
  }
  // Fallback: 12 hours later (defensive — should never hit with default config)
  return new Date(when.getTime() + 12 * 60 * 60 * 1000);
}
