'use strict';

// The calendar is the operator's only view of what is scheduled, and it disagreed with the engine
// in both directions for the two most-used repeat presets.
//
// The old expansion stepped by the recurrence unit from the schedule's original start:
//   - WEEKLY advanced a whole week at a time, so dayOfWeek never changed and a
//     FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR rule matched only its start day. Created on a Monday it drew
//     one event a week; created on a Saturday it drew nothing at all.
//   - The walk began at the original start under a 366-iteration cap, so a schedule begun more than
//     a year ago never reached the current week and drew nothing.
// Meanwhile the engine evaluates day-of-week directly, so those schedules ran Mon-Fri the whole
// time. Screens were switching content that the calendar said was not scheduled.
//
// The invariant: the calendar draws an event on every day the schedule actually fires.

const { test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('path');
const fs = require('fs');
const os = require('os');

const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'st-cal-'));
process.env.DATA_DIR = tmp;
process.env.JWT_SECRET = 'test-secret-calendar';

const { expandSchedule } = require('../routes/schedules');

// A Monday-to-Sunday window well clear of the schedules' start dates.
const WEEK_START = new Date('2026-08-03T00:00:00');   // Monday
const WEEK_END = new Date('2026-08-09T23:59:59');     // Sunday

const mk = (recurrence, startISO, recurrenceEnd = null) => ({
  id: 's', recurrence, recurrence_end: recurrenceEnd,
  start_time: startISO,
  end_time: new Date(new Date(startISO).getTime() + 60 * 60 * 1000).toISOString(),
});
const weekdaysOf = (events) => events.map(e => new Date(e.instance_start).getDay()).sort();

test('THE BUG: a Mon-Fri rule draws five events, not one', () => {
  const ev = expandSchedule(mk('FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', '2026-07-27T09:00:00'), WEEK_START, WEEK_END);
  assert.equal(ev.length, 5);
  assert.deepEqual(weekdaysOf(ev), [1, 2, 3, 4, 5], 'Mon..Fri');
});

test('...and it does not matter which day the rule was created on', () => {
  // Created on a Saturday, the old code drew nothing whatsoever.
  const ev = expandSchedule(mk('FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', '2026-08-01T09:00:00'), WEEK_START, WEEK_END);
  assert.equal(ev.length, 5);
  assert.deepEqual(weekdaysOf(ev), [1, 2, 3, 4, 5]);
});

test('a DAILY schedule older than a year still draws', () => {
  // The 366-iteration cap meant the walk never reached the visible window.
  const ev = expandSchedule(mk('FREQ=DAILY', '2024-05-01T09:00:00'), WEEK_START, WEEK_END);
  assert.equal(ev.length, 7, 'every day of the week');
});

test('WEEKLY without byDay still means "the same weekday as the start"', () => {
  const ev = expandSchedule(mk('FREQ=WEEKLY', '2026-07-27T09:00:00'), WEEK_START, WEEK_END);  // a Monday
  assert.equal(ev.length, 1);
  assert.deepEqual(weekdaysOf(ev), [1]);
});

test('a DAILY interval is honoured rather than drawn every day', () => {
  const ev = expandSchedule(mk('FREQ=DAILY;INTERVAL=2', '2026-08-03T09:00:00'), WEEK_START, WEEK_END);
  assert.equal(ev.length, 4, 'Mon, Wed, Fri, Sun');
});

test('recurrence_end stops the drawing', () => {
  const ev = expandSchedule(
    mk('FREQ=DAILY', '2026-07-27T09:00:00', '2026-08-05T23:59:59'), WEEK_START, WEEK_END);
  assert.equal(ev.length, 3, 'Mon, Tue, Wed then it ends');
});

test('a one-off schedule is unaffected', () => {
  const ev = expandSchedule(
    { id: 's', recurrence: null, start_time: '2026-08-05T09:00:00', end_time: '2026-08-05T10:00:00' },
    WEEK_START, WEEK_END);
  assert.equal(ev.length, 1);
});

test('each drawn event keeps the schedule duration', () => {
  const ev = expandSchedule(mk('FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', '2026-07-27T09:00:00'), WEEK_START, WEEK_END);
  for (const e of ev) {
    const mins = (new Date(e.instance_end) - new Date(e.instance_start)) / 60000;
    assert.equal(mins, 60);
  }
});

test.after(() => { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_) {} });
