import { addDays, addWeeks, format, getISOWeek, getISOWeekYear, startOfISOWeek } from 'date-fns';

const WEEK_CODE_RE = /^(\d{4})-W(0[1-9]|[1-4]\d|5[0-3])$/;

export type WeekCode = `${number}-W${string}`;

export function formatWeekCode(date: Date): WeekCode {
  const monday = startOfISOWeek(date);
  const isoYear = getISOWeekYear(monday);
  const isoWeek = getISOWeek(monday).toString().padStart(2, '0');
  return `${isoYear}-W${isoWeek}` as WeekCode;
}

export function parseWeekCode(weekCode: string): Date | null {
  const match = WEEK_CODE_RE.exec(weekCode);
  if (!match) return null;

  const isoYear = Number(match[1]);
  const isoWeek = Number(match[2]);

  // Jan 4 is always in ISO week 1 for a given ISO year.
  const weekOneMonday = startOfISOWeek(new Date(isoYear, 0, 4));
  const candidate = addWeeks(weekOneMonday, isoWeek - 1);

  // Reject impossible week codes (e.g. week 53 in a 52-week ISO year).
  if (getISOWeekYear(candidate) !== isoYear || getISOWeek(candidate) !== isoWeek) {
    return null;
  }

  return candidate;
}

export function getCurrentWeekCode(now = new Date()): WeekCode {
  return formatWeekCode(now);
}

export function getFourWeekRangeFromWeekCode(weekCode: string) {
  const startDate = parseWeekCode(weekCode);
  if (!startDate) return null;

  const endDate = addDays(startDate, 27);
  return {
    startDate,
    endDate,
    fromDate: format(startDate, 'yyyyMMdd'),
    toDate: format(endDate, 'yyyyMMdd'),
  };
}
