const MEXICO_TZ = "America/Mexico_City";
const DAY_MS = 24 * 60 * 60 * 1000;

function mexicoDateParts(date: Date) {
  const formatter = new Intl.DateTimeFormat("en-CA", {
    timeZone: MEXICO_TZ,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  });

  const parts = formatter.formatToParts(date);
  const year = Number(parts.find((part) => part.type === "year")?.value ?? "1970");
  const month = Number(parts.find((part) => part.type === "month")?.value ?? "01");
  const day = Number(parts.find((part) => part.type === "day")?.value ?? "01");

  return { year, month, day };
}

export function isoDateFromParts(parts: { year: number; month: number; day: number }): string {
  const month = String(parts.month).padStart(2, "0");
  const day = String(parts.day).padStart(2, "0");
  return `${parts.year}-${month}-${day}`;
}

export function mexicoTodayIso(referenceDate: Date = new Date()): string {
  return isoDateFromParts(mexicoDateParts(referenceDate));
}

export function isoToUtcDate(isoDate: string): Date {
  return new Date(`${isoDate}T00:00:00.000Z`);
}

export function addDays(isoDate: string, days: number): string {
  const next = isoToUtcDate(isoDate).getTime() + days * DAY_MS;
  return new Date(next).toISOString().slice(0, 10);
}

export function normalizeWeekStart(weekStartInput?: string | null, referenceDate: Date = new Date()): string {
  const candidate = weekStartInput && /^\d{4}-\d{2}-\d{2}$/.test(weekStartInput)
    ? weekStartInput
    : mexicoTodayIso(referenceDate);

  const day = new Date(`${candidate}T00:00:00.000Z`).getUTCDay();
  const diffToMonday = day === 0 ? -6 : 1 - day;
  return addDays(candidate, diffToMonday);
}

export function getWeekBounds(weekStartInput?: string | null, referenceDate: Date = new Date()): { weekStart: string; weekEnd: string } {
  const weekStart = normalizeWeekStart(weekStartInput, referenceDate);
  return {
    weekStart,
    weekEnd: addDays(weekStart, 6),
  };
}

export function isDueSoonIso(dueDateIso: string | null, referenceIso: string, daysAhead = 7): boolean {
  if (!dueDateIso) {
    return false;
  }

  const due = isoToUtcDate(dueDateIso).getTime();
  const current = isoToUtcDate(referenceIso).getTime();
  const horizon = current + daysAhead * DAY_MS;
  return due >= current && due <= horizon;
}

export function isOverdueIso(dueDateIso: string | null, referenceIso: string): boolean {
  if (!dueDateIso) {
    return false;
  }

  return isoToUtcDate(dueDateIso).getTime() < isoToUtcDate(referenceIso).getTime();
}
