"use client";

/**
 * Local date/time ↔ ISO helpers for `<input type="date">` and `type="time"`.
 *
 * These exist because the obvious shortcut is wrong: `iso.slice(0, 10)` reads
 * the *UTC* date, so for any timezone ahead of UTC a value stored as local
 * midnight renders as the previous day — and re-saving then walks the date
 * backwards one day per edit. That bug shipped once in TaskModal already; both
 * modals now share this module instead of each re-deriving the conversion.
 */

const pad = (n: number) => String(n).padStart(2, "0");

/** "YYYY-MM-DD" for `<input type="date">`, in the viewer's local zone. */
export function toLocalDateInput(value: Date | string): string {
  const d = value instanceof Date ? value : new Date(value);
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}

/** "HH:MM" for `<input type="time">`, in the viewer's local zone. */
export function toLocalTimeInput(value: Date | string): string {
  const d = value instanceof Date ? value : new Date(value);
  return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

/**
 * Combines the two input values back into an instant. An empty or missing
 * time means midnight local, which is what an all-day event stores.
 */
export function fromLocalDateTime(dateValue: string, timeValue?: string): string {
  const [hours, minutes] = (timeValue || "00:00").split(":").map(Number);
  const [year, month, day] = dateValue.split("-").map(Number);
  return new Date(year!, (month ?? 1) - 1, day ?? 1, hours ?? 0, minutes ?? 0, 0, 0).toISOString();
}

/** The viewer's IANA zone, stored on an event so recurrence keeps its local
 * time (see libs/core/src/recurrence.ts). Falls back to UTC on the rare
 * runtime without a resolved timeZone. */
export function localTimeZone(): string {
  try {
    return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
  } catch {
    return "UTC";
  }
}

/** Monday-start week containing `date`, as local midnight boundaries. */
export function weekRange(date: Date): { start: Date; end: Date } {
  const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  // getDay() is 0 for Sunday; shift so Monday is the first column.
  const offset = (start.getDay() + 6) % 7;
  start.setDate(start.getDate() - offset);
  const end = new Date(start);
  end.setDate(end.getDate() + 7);
  return { start, end };
}

/**
 * The Monday-aligned 6×7 block a month calendar draws: the 1st back to its
 * Monday, then always 42 days. Fixed at six rows deliberately — a month
 * needing only five would otherwise change the grid's row height as you page
 * through the year, which makes the whole view jump.
 */
export function monthGridRange(date: Date): { start: Date; end: Date } {
  const firstOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
  const { start } = weekRange(firstOfMonth);
  return { start, end: addDays(start, 42) };
}

export function addMonths(date: Date, months: number): Date {
  // Clamp to the target month's length so paging from the 31st doesn't skip a
  // month (Jan 31 → Mar 3).
  const target = new Date(date.getFullYear(), date.getMonth() + months, 1);
  const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
  target.setDate(Math.min(date.getDate(), lastDay));
  return target;
}

export function isSameMonth(a: Date, b: Date): boolean {
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
}

export function addDays(date: Date, days: number): Date {
  const out = new Date(date);
  out.setDate(out.getDate() + days);
  return out;
}

export function isSameLocalDay(a: Date | string, b: Date | string): boolean {
  return toLocalDateInput(a) === toLocalDateInput(b);
}

export function formatTimeLabel(value: Date | string): string {
  const d = value instanceof Date ? value : new Date(value);
  return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
}

/** "Today 4:05 PM" / "Yesterday 9:12 AM" / "Mar 3, 4:05 PM" — a timestamp for a
 * list where most rows are recent but the tail is not. */
export function formatWhenLabel(value: Date | string): string {
  const d = value instanceof Date ? value : new Date(value);
  const today = new Date();
  const key = toLocalDateInput(d);
  if (key === toLocalDateInput(today)) return `Today ${formatTimeLabel(d)}`;
  if (key === toLocalDateInput(addDays(today, -1))) return `Yesterday ${formatTimeLabel(d)}`;
  const sameYear = d.getFullYear() === today.getFullYear();
  const date = d.toLocaleDateString([], sameYear ? { month: "short", day: "numeric" } : { year: "numeric", month: "short", day: "numeric" });
  return `${date}, ${formatTimeLabel(d)}`;
}

/** A call length as "4m 12s" / "1h 3m" / "12s". Zero is a real value here (a
 * call nobody answered), so it renders rather than returning empty. */
export function formatDurationLabel(seconds: number): string {
  if (seconds < 60) return `${seconds}s`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
  return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
}
