/**
 * Date helpers. Reservation dates are calendar dates stored as UTC midnight so
 * they are timezone-independent. Timestamps are real instants and are
 * formatted in the business timezone.
 */

export const DAY_MS = 86_400_000;

/** Parse "YYYY-MM-DD" to UTC midnight. */
export function parseDay(s: string): Date {
  const [y, m, d] = s.split("-").map(Number);
  return new Date(Date.UTC(y, m - 1, d));
}

/** Normalise any Date to UTC midnight of its UTC calendar day. */
export function toDay(d: Date): Date {
  return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
}

/** Today's calendar date in the given timezone, as UTC midnight. */
export function todayIn(tz = "Africa/Casablanca", now = new Date()): Date {
  const parts = new Intl.DateTimeFormat("en-CA", {
    timeZone: tz,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(now); // en-CA gives YYYY-MM-DD
  return parseDay(parts);
}

export function dayKey(d: Date): string {
  return d.toISOString().slice(0, 10);
}

export function addDays(d: Date, n: number): Date {
  return new Date(d.getTime() + n * DAY_MS);
}

export function diffDays(a: Date, b: Date): number {
  return Math.round((toDay(b).getTime() - toDay(a).getTime()) / DAY_MS);
}

export function nightsBetween(checkIn: Date, checkOut: Date): number {
  return Math.max(0, diffDays(checkIn, checkOut));
}

export function startOfMonth(d: Date): Date {
  return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
}
export function endOfMonth(d: Date): Date {
  return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0));
}
export function startOfWeek(d: Date): Date {
  const day = (d.getUTCDay() + 6) % 7; // Monday = 0
  return addDays(toDay(d), -day);
}
export function startOfYear(d: Date): Date {
  return new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
}
export function addMonths(d: Date, n: number): Date {
  return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + n, d.getUTCDate()));
}

export function isWeekend(d: Date): boolean {
  const day = d.getUTCDay();
  return day === 5 || day === 6; // Friday & Saturday nights (Moroccan weekend pattern)
}

/** Iterate each night between checkIn (inclusive) and checkOut (exclusive). */
export function eachNight(checkIn: Date, checkOut: Date): Date[] {
  const out: Date[] = [];
  for (let d = toDay(checkIn); d < toDay(checkOut); d = addDays(d, 1)) out.push(d);
  return out;
}

export function overlaps(aStart: Date, aEnd: Date, bStart: Date, bEnd: Date): boolean {
  return aStart < bEnd && bStart < aEnd;
}

// ── Formatting ────────────────────────────────────────────────

export function fmtDate(d: Date | string | null | undefined, opts: { tz?: string; style?: "short" | "medium" | "long" | "weekday" } = {}): string {
  if (!d) return "—";
  const date = typeof d === "string" ? new Date(d) : d;
  const style = opts.style ?? "medium";
  const base: Intl.DateTimeFormatOptions = { timeZone: "UTC" };
  if (style === "short") return new Intl.DateTimeFormat("en-GB", { ...base, day: "2-digit", month: "2-digit", year: "2-digit" }).format(date);
  if (style === "long") return new Intl.DateTimeFormat("en-GB", { ...base, day: "numeric", month: "long", year: "numeric" }).format(date);
  if (style === "weekday") return new Intl.DateTimeFormat("en-GB", { ...base, weekday: "short", day: "numeric", month: "short" }).format(date);
  return new Intl.DateTimeFormat("en-GB", { ...base, day: "numeric", month: "short", year: "numeric" }).format(date);
}

export function fmtDateTime(d: Date | string | null | undefined, tz = "Africa/Casablanca"): string {
  if (!d) return "—";
  const date = typeof d === "string" ? new Date(d) : d;
  return new Intl.DateTimeFormat("en-GB", {
    timeZone: tz,
    day: "numeric",
    month: "short",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  }).format(date);
}

export function fmtTime(d: Date | string | null | undefined, tz = "Africa/Casablanca"): string {
  if (!d) return "—";
  const date = typeof d === "string" ? new Date(d) : d;
  return new Intl.DateTimeFormat("en-GB", { timeZone: tz, hour: "2-digit", minute: "2-digit" }).format(date);
}

export function fmtRange(a: Date | string, b: Date | string): string {
  const da = typeof a === "string" ? new Date(a) : a;
  const dbb = typeof b === "string" ? new Date(b) : b;
  const sameMonth = da.getUTCMonth() === dbb.getUTCMonth() && da.getUTCFullYear() === dbb.getUTCFullYear();
  if (sameMonth) {
    return `${da.getUTCDate()}–${dbb.getUTCDate()} ${new Intl.DateTimeFormat("en-GB", { timeZone: "UTC", month: "short", year: "numeric" }).format(dbb)}`;
  }
  return `${fmtDate(da)} → ${fmtDate(dbb)}`;
}

export function relativeTime(d: Date | string | null | undefined, now = new Date()): string {
  if (!d) return "never";
  const date = typeof d === "string" ? new Date(d) : d;
  const diff = (now.getTime() - date.getTime()) / 1000;
  if (diff < 45) return "just now";
  if (diff < 3600) return `${Math.round(diff / 60)} min ago`;
  if (diff < 86400) return `${Math.round(diff / 3600)} h ago`;
  if (diff < 86400 * 7) return `${Math.round(diff / 86400)} d ago`;
  return fmtDate(date);
}
