// Date helpers + sample habit-entry generator.
// We model entries as a Map<isoDate, status> per member, where status ∈ 'done'|'miss'|'today'|'future'.
// All dates anchored to "today" = the day the page was loaded; everything is computed as offsets.

// Treat dates as plain UTC ymd (no time-of-day) so we can use them as Map keys.
function ymd(d) {
  // Local-tz Y-M-D string
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}
function dateFromYmd(s) {
  const [y, m, d] = s.split('-').map(Number);
  return new Date(y, m - 1, d);
}
function addDays(d, n) {
  const out = new Date(d);
  out.setDate(out.getDate() + n);
  return out;
}
function diffDays(a, b) {
  // whole-day diff: a - b
  const A = new Date(a.getFullYear(), a.getMonth(), a.getDate()).getTime();
  const B = new Date(b.getFullYear(), b.getMonth(), b.getDate()).getTime();
  return Math.round((A - B) / 86400000);
}
function startOfWeekMon(d) {
  // Mon=0 ... Sun=6 (ISO-style week starting Monday)
  const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const day = (x.getDay() + 6) % 7; // 0=Mon
  x.setDate(x.getDate() - day);
  return x;
}
function isoWeekNumber(d) {
  // ISO 8601 week number
  const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const dayNum = (x.getDay() + 6) % 7;
  x.setDate(x.getDate() - dayNum + 3); // nearest Thursday
  const firstThu = new Date(x.getFullYear(), 0, 4);
  const firstThuDay = (firstThu.getDay() + 6) % 7;
  firstThu.setDate(firstThu.getDate() - firstThuDay + 3);
  return 1 + Math.round((x - firstThu) / (7 * 86400000));
}
const MONTH_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const DAY_LETTER = ['M','T','W','T','F','S','S']; // Mon..Sun

// Deterministic PRNG so entries don't shuffle on every render.
function mulberry32(seed) {
  let a = seed | 0;
  return () => {
    a = (a + 0x6d2b79f5) | 0;
    let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

// For a member, generate a status map covering the past `daysBack` days up to today.
// `compliance` is roughly the probability of "done" on any given relevant day.
// `cadence` controls which days are *relevant* (others = 'rest' = no chip rendered):
//   - 'daily': every day
//   - 'set-days': only on the listed weekday-mons (Mon=0..Sun=6)
//   - 'n-per-week': any n days per ISO-week (random within each week)
//   - 'weekly': one day per week (the configured weekday)
function generateEntries({ today, daysBack = 120, seed, compliance = 0.85, cadence, recentOverride = {} }) {
  const rng = mulberry32(seed);
  const entries = {};

  // Build the set of "relevant" days
  const relevantSet = new Set();
  if (cadence.kind === 'daily') {
    for (let i = 0; i <= daysBack; i++) {
      relevantSet.add(ymd(addDays(today, -i)));
    }
  } else if (cadence.kind === 'set-days') {
    const wantedMons = new Set(cadence.weekdays); // 0..6 (Mon..Sun)
    for (let i = 0; i <= daysBack; i++) {
      const d = addDays(today, -i);
      const mon = (d.getDay() + 6) % 7;
      if (wantedMons.has(mon)) relevantSet.add(ymd(d));
    }
  } else if (cadence.kind === 'n-per-week') {
    // Walk week by week; for each week, pick `n` random distinct weekdays.
    // We need to also include the *current* week even if today is mid-week.
    let weekStart = startOfWeekMon(today);
    const earliest = startOfWeekMon(addDays(today, -daysBack));
    while (weekStart >= earliest) {
      // pick n distinct weekday offsets
      const picks = new Set();
      while (picks.size < cadence.n) picks.add(Math.floor(rng() * 7));
      for (const wd of picks) {
        const d = addDays(weekStart, wd);
        if (d <= today) relevantSet.add(ymd(d));
      }
      weekStart = addDays(weekStart, -7);
    }
  } else if (cadence.kind === 'weekly') {
    const wantedMon = cadence.weekday; // 0..6
    let weekStart = startOfWeekMon(today);
    const earliest = startOfWeekMon(addDays(today, -daysBack));
    while (weekStart >= earliest) {
      const d = addDays(weekStart, wantedMon);
      if (d <= today) relevantSet.add(ymd(d));
      weekStart = addDays(weekStart, -7);
    }
  }

  // Assign status to each relevant day
  for (const key of relevantSet) {
    const d = dateFromYmd(key);
    const isToday = ymd(d) === ymd(today);
    if (isToday) {
      entries[key] = 'today';
    } else {
      entries[key] = rng() < compliance ? 'done' : 'miss';
    }
  }

  // Apply hand-tuned overrides for the most recent days so the demo has nice patterns
  for (const [k, v] of Object.entries(recentOverride)) {
    entries[k] = v;
  }

  return { entries, relevantSet };
}

Object.assign(window, {
  ymd, dateFromYmd, addDays, diffDays, startOfWeekMon, isoWeekNumber,
  MONTH_SHORT, DAY_LETTER,
  generateEntries,
});
