// Builds the array of cells for a given cadence, going back daysBack from today.
// Each cell has:
//   key            — stable id
//   label          — main text (date number or "W20")
//   subLabel?      — optional secondary text (weekday letter for set-days)
//   monthFlag?     — month abbreviation (e.g. "Nov") shown on month-boundary cells
//   isToday        — true if cell represents today (or this week for week cells)
//   isFuture       — cell is in the future
//   getStatus(member) — returns 'done'|'miss'|'today'|'future'|null for the cell
//   meta           — { kind, date | weekStart, weekDates? } so renderers can do more

function buildCells({ today, cadence, daysBack = 90 }) {
  if (cadence.kind === 'daily') return buildDailyCells({ today, daysBack });
  if (cadence.kind === 'set-days') return buildSetDayCells({ today, cadence, daysBack });
  if (cadence.kind === 'weekly') return buildWeeklyCells({ today, cadence, daysBack });
  if (cadence.kind === 'n-per-week') return buildNPerWeekCells({ today, cadence, daysBack });
  return [];
}

function buildDailyCells({ today, daysBack }) {
  // Cells go from oldest -> today + 1 (tomorrow as a future cell on the right).
  const cells = [];
  const start = addDays(today, -daysBack);
  const end = addDays(today, 1); // include tomorrow
  let cursor = new Date(start);
  let prevMonth = -1;
  while (cursor <= end) {
    const isToday = ymd(cursor) === ymd(today);
    const isFuture = cursor > today;
    const monthFlag = cursor.getMonth() !== prevMonth ? MONTH_SHORT[cursor.getMonth()] : null;
    prevMonth = cursor.getMonth();
    const dateStr = ymd(cursor);
    const cellDate = new Date(cursor);
    cells.push({
      key: dateStr,
      label: String(cursor.getDate()),
      monthFlag,
      isToday,
      isFuture,
      getStatus: (m) => m.entries[dateStr] || (isFuture ? 'future' : null),
      meta: { kind: 'day', date: cellDate, dateStr },
    });
    cursor = addDays(cursor, 1);
  }
  return cells;
}

function buildSetDayCells({ today, cadence, daysBack }) {
  // Show only the listed weekdays. Label = letter + date.
  // Includes ONE upcoming cell after today (the next configured weekday).
  const wantedMons = new Set(cadence.weekdays); // 0..6 (Mon..Sun)
  const cells = [];
  // Find next-after-today wanted weekday (look up to 14 days ahead)
  let nextFuture = null;
  for (let i = 1; i <= 14; i++) {
    const d = addDays(today, i);
    const mon = (d.getDay() + 6) % 7;
    if (wantedMons.has(mon)) { nextFuture = d; break; }
  }
  const end = nextFuture || today;
  let cursor = addDays(today, -daysBack);
  let prevMonth = -1;
  while (cursor <= end) {
    const mon = (cursor.getDay() + 6) % 7;
    if (wantedMons.has(mon)) {
      const isToday = ymd(cursor) === ymd(today);
      const isFuture = cursor > today;
      const monthFlag = cursor.getMonth() !== prevMonth ? MONTH_SHORT[cursor.getMonth()] : null;
      prevMonth = cursor.getMonth();
      const dateStr = ymd(cursor);
      const cellDate = new Date(cursor);
      cells.push({
        key: dateStr,
        label: String(cursor.getDate()),
        subLabel: DAY_LETTER[mon],
        monthFlag,
        isToday,
        isFuture,
        smallLabel: true,
        getStatus: (m) => m.entries[dateStr] || (isFuture ? 'future' : null),
        meta: { kind: 'day', date: cellDate, dateStr },
      });
    }
    cursor = addDays(cursor, 1);
  }
  return cells;
}

function buildWeeklyCells({ today, cadence, daysBack }) {
  // 1-per-week: each cell = a week. Includes NEXT week as a future cell on the right.
  const cells = [];
  const earliest = startOfWeekMon(addDays(today, -daysBack));
  const thisWeekStart = startOfWeekMon(today);
  // walk back from this week, then add one week ahead
  const starts = [];
  let weekStart = thisWeekStart;
  while (weekStart >= earliest) {
    starts.push(new Date(weekStart));
    weekStart = addDays(weekStart, -7);
  }
  starts.reverse();
  starts.push(addDays(thisWeekStart, 7)); // next week
  for (const ws of starts) {
    const targetDate = addDays(ws, cadence.weekday);
    const isThisWeek = ws.getTime() === startOfWeekMon(today).getTime();
    const dateStr = ymd(targetDate);
    cells.push({
      key: 'w-' + ymd(ws),
      label: 'W' + isoWeekNumber(ws),
      isToday: isThisWeek,
      isFuture: targetDate > today,
      smallLabel: true,
      getStatus: (m) => m.entries[dateStr] || (targetDate > today ? 'future' : 'miss'),
      meta: { kind: 'week', weekStart: ws, targetDate, dateStr },
    });
  }
  return cells;
}

function buildNPerWeekCells({ today, cadence, daysBack }) {
  // Each cell = a week, rendered as ONE constant-size circle (same footprint as
  // a daily cell), NOT a row of tiny pips. A week is "done" when the member met
  // their weekly target (count >= n). getStatus returns a plain status string so
  // the normal DayPie / DayCircle renderers handle it (fixed ~50px circle,
  // fewer weeks visible + horizontal scroll — never shrunk to cram more in).
  const cells = [];
  const earliest = startOfWeekMon(addDays(today, -daysBack));
  let weekStart = startOfWeekMon(today);
  const starts = [];
  while (weekStart >= earliest) {
    starts.push(new Date(weekStart));
    weekStart = addDays(weekStart, -7);
  }
  starts.reverse();
  for (const ws of starts) {
    const isThisWeek = ws.getTime() === startOfWeekMon(today).getTime();
    // get the 7 day strings for this week
    const weekDateStrs = [];
    for (let i = 0; i < 7; i++) weekDateStrs.push(ymd(addDays(ws, i)));
    // A representative day for selection / check-in on this week: today for the
    // current week, otherwise the week's Sunday (its last day, always <= today).
    const repDateStr = isThisWeek ? ymd(today) : ymd(addDays(ws, 6));
    cells.push({
      key: 'w-' + ymd(ws),
      label: 'W' + isoWeekNumber(ws),
      isToday: isThisWeek,
      isFuture: false,
      smallLabel: true,
      getStatus: (m) => {
        // count done entries within this week
        const c = weekDateStrs.reduce((acc, d) => acc + (m.entries[d] === 'done' ? 1 : 0), 0);
        if (c >= cadence.n) return 'done';
        return isThisWeek ? 'today' : 'miss'; // current week still in progress vs. a past week that fell short
      },
      // dateStr = the representative day so DayPie/selection/highlight all work.
      meta: { kind: 'week-n', weekStart: ws, weekDateStrs, n: cadence.n, dateStr: repDateStr },
    });
  }
  return cells;
}

Object.assign(window, { buildCells });
