import { mapToScheduleDays } from './map-to-schedule-days';
import { TaskCopy, TaskWithDueTime } from '../../tasks/task.model';
import { TaskRepeatCfg } from '../../task-repeat-cfg/task-repeat-cfg.model';
import { getDbDateStr } from '../../../util/get-db-date-str';

const NDS = '1970-01-01';
const N = new Date(1970, 0, 1, 0, 0, 0, 0).getTime();
// 86400000 ==> 24h
const H = 60 * 60 * 1000;
const TZ_OFFSET = new Date(NDS).getTimezoneOffset() * 60000;
// const TZ_OFFSET = 0;
console.log('TZ_OFFSET', TZ_OFFSET);

// Helper function to conditionally skip tests that are timezone-dependent
// These tests were written with hardcoded expectations for Europe/Berlin timezone
const isEuropeBerlinTimezone = (): boolean => TZ_OFFSET === -3600000; // UTC+1 = -1 hour offset
const maybeSkipTimezoneDependent = (testName: string): boolean => {
  if (!isEuropeBerlinTimezone()) {
    console.warn(
      `Skipping timezone-dependent test "${testName}" - only runs in Europe/Berlin timezone`,
    );
    return true;
  }
  return false;
};

const FAKE_TASK: Partial<TaskCopy> = {
  tagIds: [],
  subTaskIds: [],
  timeSpent: 0,
  timeEstimate: H,
};

const h = (hr: number): number => hr * 60 * 1000 * 60;
const hTz = (hr: number): number => h(hr) + TZ_OFFSET;
// eslint-disable-next-line no-mixed-operators
const dh = (d: number = 0, hr: number): number => hr * H + d * h(24);
const dhTz = (d: number = 0, hr: number): number => dh(d, hr) + TZ_OFFSET;

const minAfterNow = (min: number): Date => new Date(1970, 0, 1, 0, min, 0, 0);

const minAfterNowTs = (min: number): number => minAfterNow(min).getTime();

const fakeTaskEntry = (id = 'XXX', add?: Partial<TaskCopy>): TaskCopy => {
  return {
    ...FAKE_TASK,
    ...add,
    id,
  } as TaskCopy;
};

const fakePlannedTaskEntry = (
  id = 'XXX',
  planedAt: Date,
  add?: Partial<TaskWithDueTime>,
): TaskWithDueTime => {
  return {
    ...fakeTaskEntry(id, add),
    dueWithTime: planedAt.getTime(),
    reminderId: 'R_ID',
  } as TaskWithDueTime;
};

const fakeRepeatCfg = (
  id = 'R_CFG_1',
  startTime?: string,
  add?: Partial<TaskRepeatCfg>,
): TaskRepeatCfg => {
  return {
    startDate: '1969-01-01',
    startTime,
    // eslint-disable-next-line no-mixed-operators
    lastTaskCreationDay: getDbDateStr(N - 24 * 60 * 60 * 1000),
    monday: true,
    tuesday: true,
    wednesday: true,
    thursday: true,
    friday: true,
    saturday: true,
    sunday: true,
    repeatCycle: 'DAILY',
    repeatEvery: 1,
    defaultEstimate: h(1),
    ...add,
    id,
  } as Partial<TaskRepeatCfg> as TaskRepeatCfg;
};

describe('mapToScheduleDays()', () => {
  it('should work for empty case', () => {
    expect(
      mapToScheduleDays(N, [], [], [], [], [], [], null, {}, undefined, undefined),
    ).toEqual([]);
  });

  it('should work for simple case', () => {
    expect(
      mapToScheduleDays(
        N,
        [NDS, '2020-1-2'],
        [
          fakeTaskEntry('1', { timeEstimate: h(1) }),
          fakeTaskEntry('2', { timeEstimate: h(2) }),
        ],
        [],
        [],
        [],
        [],
        null,
        {},
        undefined,
        undefined,
      ),
    ).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: NDS,
        entries: [
          {
            data: {
              ...FAKE_TASK,
              id: '1',
              timeEstimate: h(1),
              timeSpent: 0,
            },
            id: '1',
            start: hTz(0),
            duration: h(1),
            type: 'Task',
          },
          {
            data: {
              ...FAKE_TASK,
              id: '2',
              timeEstimate: h(2),
              timeSpent: 0,
            },
            id: '2',
            start: hTz(1),
            duration: h(2),
            type: 'Task',
          },
        ],
        isToday: true,
      },
      { beyondBudgetTasks: [], dayDate: '2020-1-2', entries: [], isToday: false },
    ] as any);
  });

  it('should work for simple scheduled case', () => {
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02'],
      [
        fakeTaskEntry('N1', { timeEstimate: h(1) }),
        // fakeTaskEntry('2', { timeEstimate: h(2 }),
      ],
      [fakePlannedTaskEntry('S1', minAfterNow(30), { timeEstimate: h(1) })],
      [],
      [],
      [],
      null,
      {},
      undefined,
      undefined,
    );
    expect(r[0].entries.length).toBe(3);
    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: {
              id: 'N1',
              subTaskIds: [],
              tagIds: [],
              timeEstimate: 3600000,
              timeSpent: 0,
            },
            id: 'N1',
            start: hTz(0),
            duration: h(0.5),
            type: 'SplitTask',
          },
          {
            data: {
              id: 'S1',
              dueWithTime: minAfterNowTs(30),
              reminderId: 'R_ID',
              subTaskIds: [],
              tagIds: [],
              timeEstimate: h(1),
              timeSpent: 0,
            },
            id: 'S1',
            start: minAfterNowTs(30),
            duration: h(1),
            type: 'ScheduledTask',
          },
          {
            data: {
              id: 'N1',
              subTaskIds: [],
              tagIds: [],
              timeEstimate: 3600000,
              timeSpent: 0,
            },
            id: 'N1_1970-01-01_0',
            start: minAfterNowTs(90),
            duration: h(0.5),
            type: 'SplitTaskContinuedLast',
          },
        ],
        isToday: true,
      },
      { beyondBudgetTasks: [], dayDate: '1970-01-02', entries: [], isToday: false },
    ] as any);
  });

  it('should work for very long scheduled tasks', () => {
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02'],
      [fakeTaskEntry('N1', { timeEstimate: h(1) })],
      [fakePlannedTaskEntry('S1', new Date(dhTz(0, 9)), { timeEstimate: h(9) })],
      [],
      [],
      [],
      null,
      {},
      { startTime: '9:00', endTime: '18:00' },
      undefined,
    );
    expect(r.length).toBe(2);

    expect(r[0].entries.length).toBe(1);
    expect(r[0].entries[0].start).toBe(dhTz(0, 9));
    expect(r[0].entries[0].duration).toBe(h(9));

    expect(r[1].entries.length).toBe(1);
    expect(r[1].entries[0].start).toBe(dhTz(1, 9));
    expect(r[1].entries[0].duration).toBe(h(1));
  });

  it('should split multiple times', () => {
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02'],
      [
        fakeTaskEntry('N1', { timeEstimate: h(2) }),
        // fakeTaskEntry('2', { timeEstimate: h(2 }),
      ],
      [
        fakePlannedTaskEntry('S1', minAfterNow(30), { timeEstimate: h(0.5) }),
        fakePlannedTaskEntry('S2', minAfterNow(90), { timeEstimate: h(0.5) }),
      ],
      [],
      [],
      [],
      null,
      {},
      undefined,
      undefined,
    );
    expect(r[0].entries.length).toBe(5);
    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N1',
            start: hTz(0),
            duration: h(0.5),
            type: 'SplitTask',
          },
          {
            data: jasmine.any(Object),
            id: 'S1',
            start: minAfterNowTs(30),
            duration: h(0.5),
            type: 'ScheduledTask',
          },
          {
            data: jasmine.any(Object),
            id: 'N1_1970-01-01_0',
            start: minAfterNowTs(60),
            duration: h(0.5),
            type: 'SplitTaskContinued',
          },
          {
            data: jasmine.any(Object),
            id: 'S2',
            start: minAfterNowTs(90),
            duration: h(0.5),
            type: 'ScheduledTask',
          },
          {
            data: jasmine.any(Object),
            id: 'N1_1970-01-01_1',
            start: minAfterNowTs(120),
            duration: h(1),
            type: 'SplitTaskContinuedLast',
          },
        ],
        isToday: true,
      },
      { beyondBudgetTasks: [], dayDate: '1970-01-02', entries: [], isToday: false },
    ] as any);
  });

  //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  it('should show repeat for next day', () => {
    if (maybeSkipTimezoneDependent('should show repeat for next day')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02'],
      [
        fakeTaskEntry('N1', { timeEstimate: h(2) }),
        // fakeTaskEntry('2', { timeEstimate: h(2 }),
      ],
      [],
      // [fakePlannedTaskEntry('S1', minAfterNow(30), { timeEstimate: h(1 })],
      [fakeRepeatCfg('R1', '01:00', { defaultEstimate: h(1) })],
      [],
      [],
      null,
      {},
      undefined,
      undefined,
    );

    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N1',
            start: hTz(0),
            duration: h(2),
            type: 'Task',
          },
        ],
        isToday: true,
      },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-02',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'R1_1970-01-02',
            start: h(24),
            duration: h(1),
            type: 'ScheduledRepeatProjection',
            plannedForDay: '1970-01-02',
          },
        ],
        isToday: false,
      },
    ] as any);
  });

  it('should spit around scheduled repeat task cases', () => {
    if (maybeSkipTimezoneDependent('should spit around scheduled repeat task cases')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N + h(1),
      [NDS, '1970-01-02'],
      [
        // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
        // NOTE2: it is 23 since we're at 1:00 in the current time zone
        fakeTaskEntry('N1', { timeEstimate: h(23) - 60000 }),
        fakeTaskEntry('N2', { timeEstimate: h(2) }),
        // fakeTaskEntry('2', { timeEstimate: h(2 }),
      ],
      [],
      // [fakePlannedTaskEntry('S1', minAfterNow(30), { timeEstimate: h(1 })],
      [fakeRepeatCfg('R1', '01:00', { defaultEstimate: h(1) })],
      [],
      [],
      null,
      {},
      undefined,
      undefined,
    );

    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N1',
            // NOTE: the 24h stuff only works if we count from 0 of the current timezone
            start: h(0),

            duration: h(23) - 60000,
            type: 'Task',
          },
        ],
        isToday: true,
      },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-02',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N2',

            start: hTz(24),
            duration: h(1),
            type: 'SplitTask',
          },
          {
            data: jasmine.any(Object),
            id: 'R1_1970-01-02',

            start: hTz(25),
            duration: h(1),
            type: 'ScheduledRepeatProjection',
            plannedForDay: '1970-01-02',
          },
          {
            data: jasmine.any(Object),
            id: 'N2_1970-01-02_0',

            start: hTz(26),
            duration: h(1),
            type: 'SplitTaskContinuedLast',
          },
        ],
        isToday: false,
      },
    ] as any);
  });

  it('should work for NON-scheduled repeat task cases', () => {
    if (maybeSkipTimezoneDependent('should work for NON-scheduled repeat task cases')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N + TZ_OFFSET,
      [NDS, '1970-01-02'],
      [
        // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
        fakeTaskEntry('N1', { timeEstimate: hTz(24) - 60000 }),
        fakeTaskEntry('N2', { timeEstimate: h(1) }),
      ],
      [],
      [],
      [
        fakeRepeatCfg('R1', undefined, {
          defaultEstimate: h(2),
          lastTaskCreationDay: getDbDateStr(N + 60000),
        }),
      ],
      [],
      null,
      {},
      undefined,
      undefined,
    );

    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N1',
            start: hTz(0),

            duration: h(23) - 60000,
            type: 'Task',
          },
        ],
        isToday: true,
      },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-02',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'R1_1970-01-02',

            start: hTz(24),
            duration: h(2),
            type: 'RepeatProjection',
            plannedForDay: '1970-01-02',
          },
          {
            data: jasmine.any(Object),
            id: 'N2',
            // eslint-disable-next-line no-mixed-operators
            start: 26 * H + TZ_OFFSET,
            duration: h(1),
            type: 'Task',
          },
        ],
        isToday: false,
      },
    ] as any);
  });

  it('should work for for tasks spanning multiple days', () => {
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02', '1970-01-03'],
      [
        // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
        fakeTaskEntry('N1', { timeEstimate: h(16) }),
      ],
      [],
      [
        fakeRepeatCfg('R1', undefined, {
          defaultEstimate: h(2),
          startTime: '9:00',
          lastTaskCreationDay: getDbDateStr(N + 60000),
        }),
      ],
      [],
      [],
      null,
      {},
      {
        startTime: '9:00',
        endTime: '17:00',
      },
      undefined,
    );

    expect(r[0]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-01',
      entries: [
        {
          data: jasmine.any(Object),
          id: 'N1',
          start: dhTz(0, 9),

          duration: h(8),
          type: 'SplitTask',
        },
      ],
      isToday: true,
    } as any);

    expect(r[1]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-02',
      entries: [
        {
          data: jasmine.any(Object),
          duration: h(2),
          id: 'R1_1970-01-02',
          start: dhTz(1, 9),
          type: 'ScheduledRepeatProjection',
          plannedForDay: '1970-01-02',
        },
        {
          data: jasmine.any(Object),
          duration: h(6),
          id: 'N1_1970-01-01_0',
          start: dhTz(1, 11),
          type: 'SplitTaskContinued',
        },
      ],
      isToday: false,
    } as any);

    expect(r[2]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-03',
      entries: [
        {
          data: jasmine.any(Object),
          duration: h(2),
          id: 'R1_1970-01-03',
          start: dhTz(2, 9),
          type: 'ScheduledRepeatProjection',
          plannedForDay: '1970-01-03',
        },
        {
          data: jasmine.any(Object),
          duration: h(2),
          id: 'N1_1970-01-02_1',
          start: dhTz(2, 11),
          type: 'SplitTaskContinuedLast',
        },
      ],
      isToday: false,
    } as any);
  });

  it('should sort in planned tasks to their days', () => {
    if (maybeSkipTimezoneDependent('should sort in planned tasks to their days')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02', '1970-01-03', '1970-01-04'],
      [
        // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
        fakeTaskEntry('N1', { timeEstimate: h(2) }),
      ],
      [],
      [],
      [],
      [],
      null,
      {
        // eslint-disable-next-line @typescript-eslint/naming-convention
        '1970-01-02': [
          fakeTaskEntry('FD1', { timeEstimate: h(1) }),
          fakeTaskEntry('FD2', { timeEstimate: h(2) }),
        ],
        // eslint-disable-next-line @typescript-eslint/naming-convention
        '1970-01-04': [
          fakeTaskEntry('FD3', { timeEstimate: h(1) }),
          fakeTaskEntry('FD4', { timeEstimate: h(0.5) }),
        ],
      },
      undefined,
      undefined,
    );

    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'N1',
            start: hTz(0),
            duration: h(2),
            type: 'Task',
          },
        ],
        isToday: true,
      },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-02',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'FD1',
            start: 82800000,
            duration: h(1),
            type: 'TaskPlannedForDay',
          },
          {
            data: jasmine.any(Object),
            id: 'FD2',
            start: 86400000,
            duration: h(2),
            type: 'TaskPlannedForDay',
          },
        ],
        isToday: false,
      },
      { beyondBudgetTasks: [], dayDate: '1970-01-03', entries: [], isToday: false },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-04',
        entries: [
          {
            data: jasmine.any(Object),
            id: 'FD3',
            start: 255600000,
            duration: h(1),
            type: 'TaskPlannedForDay',
          },
          {
            data: jasmine.any(Object),
            id: 'FD4',
            start: 259200000,
            duration: h(0.5),
            type: 'TaskPlannedForDay',
          },
        ],
        isToday: false,
      },
    ] as any);
  });

  it('should calculate the right duration of repeat task projections', () => {
    if (
      maybeSkipTimezoneDependent(
        'should calculate the right duration of repeat task projections',
      )
    ) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02'],
      [
        // fakeTaskEntry('N1', { timeEstimate: 2 * H }),
        // fakeTaskEntry('2', { timeEstimate: 2 * H }),
      ],
      [],
      // [fakePlannedTaskEntry('S1', minAfterNow(30), { timeEstimate: H })],
      [],
      [fakeRepeatCfg('R1', undefined, { defaultEstimate: h(4) })],
      [],
      null,
      {},
      {
        startTime: '9:00',
        endTime: '17:00',
      },
      {
        startTime: '12:00',
        endTime: '13:00',
      },
    );

    expect(r).toEqual([
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-01',
        entries: [
          {
            data: {
              defaultEstimate: h(4),
              friday: true,
              id: 'R1',
              lastTaskCreationDay: '1969-12-31',
              monday: true,
              repeatCycle: 'DAILY',
              repeatEvery: 1,
              saturday: true,
              startDate: '1969-01-01',
              startTime: undefined,
              sunday: true,
              thursday: true,
              tuesday: true,
              wednesday: true,
            },
            duration: h(3),
            id: 'R1_1970-01-01',
            start: hTz(9),
            type: 'RepeatProjectionSplit',
            plannedForDay: '1970-01-01',
          },
          {
            data: { endTime: '13:00', startTime: '12:00' },
            duration: h(1),
            id: 'LUNCH_BREAK_1970-01-01',
            start: hTz(12),
            type: 'LunchBreak',
          },
          {
            data: jasmine.any(Object),
            duration: h(1),
            id: 'R1_1970-01-01_0',
            start: hTz(13),
            type: 'RepeatProjectionSplitContinuedLast',
            splitIndex: 0,
            plannedForDay: '1970-01-01',
          },
        ],
        isToday: true,
      },
      {
        beyondBudgetTasks: [],
        dayDate: '1970-01-02',
        entries: [
          {
            data: {
              defaultEstimate: h(4),
              friday: true,
              id: 'R1',
              lastTaskCreationDay: jasmine.any(String),
              monday: true,
              repeatCycle: 'DAILY',
              repeatEvery: 1,
              saturday: true,
              startDate: '1969-01-01',
              startTime: undefined,
              sunday: true,
              thursday: true,
              tuesday: true,
              wednesday: true,
            },
            duration: h(3),
            id: 'R1_1970-01-02',
            start: 115200000,
            type: 'RepeatProjectionSplit',
            plannedForDay: '1970-01-02',
          },
          {
            data: { endTime: '13:00', startTime: '12:00' },
            duration: h(1),
            id: 'LUNCH_BREAK_1970-01-02',
            start: 126000000,
            type: 'LunchBreak',
          },
          {
            data: jasmine.any(Object),
            duration: h(1),
            splitIndex: 0,
            id: 'R1_1970-01-02_0',
            start: 129600000,
            type: 'RepeatProjectionSplitContinuedLast',
            plannedForDay: '1970-01-02',
          },
        ],
        isToday: false,
      },
    ] as any);
  });

  it('should work for an example with all the stuff', () => {
    if (maybeSkipTimezoneDependent('should work for an example with all the stuff')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const r = mapToScheduleDays(
      N,
      [NDS, '1970-01-02', '1970-01-03', '1970-01-04'],
      [
        // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
        fakeTaskEntry('N1', { timeEstimate: h(2) }),
      ],
      [
        // BEFORE WORK
        fakePlannedTaskEntry('S1', minAfterNow(2 * 60), { timeEstimate: h(1) }),
        // NEXT DAY AT 10
        fakePlannedTaskEntry('S2', minAfterNow((10 + 24) * 60), { timeEstimate: h(0.5) }),
      ],
      [fakeRepeatCfg('R1', '01:00', { defaultEstimate: h(1) })],
      [
        fakeRepeatCfg('R2', undefined, {
          defaultEstimate: h(2),
          lastTaskCreationDay: getDbDateStr(N + 60000),
        }),
      ],
      [],
      null,
      {
        // eslint-disable-next-line @typescript-eslint/naming-convention
        '1970-01-02': [
          fakeTaskEntry('FD1', { timeEstimate: h(4) }),
          fakeTaskEntry('FD2', { timeEstimate: h(2) }),
        ],
        // eslint-disable-next-line @typescript-eslint/naming-convention
        '1970-01-04': [
          fakeTaskEntry('FD3', { timeEstimate: h(1) }),
          fakeTaskEntry('FD4', { timeEstimate: h(0.5) }),
        ],
      },
      {
        startTime: '9:00',
        endTime: '17:00',
      },
      {
        startTime: '12:00',
        endTime: '13:00',
      },
    );

    expect(r[0]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-01',
      entries: [
        {
          data: jasmine.any(Object),
          id: 'S1',
          start: hTz(2),
          duration: h(1),
          type: 'ScheduledTask',
        },
        {
          data: jasmine.any(Object),
          id: 'N1',
          start: hTz(9),
          duration: h(2),
          type: 'Task',
        },
        {
          data: jasmine.any(Object),
          id: 'LUNCH_BREAK_1970-01-01',
          start: hTz(12),
          duration: h(1),
          type: 'LunchBreak',
        },
      ],
      isToday: true,
    } as any);

    expect(r[1]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-02',
      entries: [
        {
          data: jasmine.any(Object),
          duration: h(1),
          id: 'R1_1970-01-02',
          start: dhTz(1, 1),
          type: 'ScheduledRepeatProjection',
          plannedForDay: '1970-01-02',
        },
        {
          data: jasmine.any(Object),
          duration: h(1),
          id: 'R2_1970-01-02',
          start: dhTz(1, 9),
          type: 'RepeatProjectionSplit',
          plannedForDay: '1970-01-02',
        },
        {
          data: jasmine.any(Object),
          id: 'S2',
          start: dhTz(1, 10),
          duration: h(0.5),
          type: 'ScheduledTask',
        },
        {
          data: jasmine.any(Object),
          duration: h(1),
          id: 'R2_1970-01-02_0',
          start: dhTz(1, 10.5),
          type: 'RepeatProjectionSplitContinuedLast',
          splitIndex: 0,
          plannedForDay: '1970-01-02',
        },
        {
          data: jasmine.any(Object),
          id: 'FD1',
          start: dhTz(1, 11.5),
          duration: h(0.5),
          type: 'SplitTaskPlannedForDay',
        },
        {
          data: jasmine.any(Object),
          id: 'LUNCH_BREAK_1970-01-02',
          start: dhTz(1, 12),
          duration: h(1),
          type: 'LunchBreak',
        },
        {
          data: jasmine.any(Object),
          id: 'FD1_1970-01-02_0',
          start: dhTz(1, 13),
          duration: h(3.5),
          type: 'SplitTaskContinuedLast',
        },
        {
          data: jasmine.any(Object),
          id: 'FD2',
          start: dhTz(1, 16.5),
          duration: h(0.5),
          type: 'SplitTaskPlannedForDay',
          isBeyondBudget: true,
        },
      ],
      isToday: false,
    } as any);

    expect(r[2]).toEqual({
      beyondBudgetTasks: [],
      dayDate: '1970-01-03',
      entries: [
        {
          data: {
            defaultEstimate: 3600000,
            friday: true,
            id: 'R1',
            lastTaskCreationDay: jasmine.any(String),
            monday: true,
            repeatCycle: 'DAILY',
            repeatEvery: 1,
            saturday: true,
            startDate: '1969-01-01',
            startTime: '01:00',
            sunday: true,
            thursday: true,
            tuesday: true,
            wednesday: true,
          },
          duration: h(1),
          id: 'R1_1970-01-03',
          start: dhTz(2, 1),
          type: 'ScheduledRepeatProjection',
          plannedForDay: '1970-01-03',
        },
        {
          data: {
            defaultEstimate: 7200000,
            friday: true,
            id: 'R2',
            lastTaskCreationDay: jasmine.any(String),
            monday: true,
            repeatCycle: 'DAILY',
            repeatEvery: 1,
            saturday: true,
            startDate: '1969-01-01',
            startTime: undefined,
            sunday: true,
            thursday: true,
            tuesday: true,
            wednesday: true,
          },
          duration: h(2),
          id: 'R2_1970-01-03',
          start: dhTz(2, 9),
          type: 'RepeatProjection',
          plannedForDay: '1970-01-03',
        },
        {
          data: { endTime: '13:00', startTime: '12:00' },
          duration: h(1),
          id: 'LUNCH_BREAK_1970-01-03',
          // start: 212400000,
          start: dhTz(2, 12),
          type: 'LunchBreak',
        },
      ],
      isToday: false,
    } as any);
  });

  it('should show recurring tasks for future weeks even if they match current day of week', () => {
    if (maybeSkipTimezoneDependent('should show recurring tasks for future weeks')) {
      pending('Skipping timezone-dependent test');
      return;
    }
    const oneWeekInMs = 7 * 24 * 60 * 60 * 1000;
    const futureThursdayTs = N + oneWeekInMs;
    const futureThursdayStr = getDbDateStr(futureThursdayTs);

    const r = mapToScheduleDays(
      futureThursdayTs,
      [futureThursdayStr],
      [],
      [],
      [fakeRepeatCfg('R1', '10:00', { defaultEstimate: h(1) })], // A recurring task
      [],
      [],
      null,
      {},
      undefined,
      undefined,
      N,
    );

    expect(r[0].entries.length).toBe(1);
    expect(r[0].entries[0].id).toBe('R1_1970-01-08');
    expect(r[0].dayDate).toBe(futureThursdayStr);
  });

  // TODO we can use this for testing the beyond budget tasks mode
  //
  // it('UNTESTED SO CHECK IF FAILING  should work for an example with all the stuff', () => {
  //   const r = mapToScheduleDays(
  //     N,
  //     [NDS, '1970-01-02', '1970-01-03', '1970-01-04'],
  //     [
  //       // NOTE: takes us to the next day, since without dayStart and dayEnd it otherwise won't
  //       fakeTaskEntry('N1', { timeEstimate: h(2) }),
  //     ],
  //     [
  //       // BEFORE WORK
  //       fakePlannedTaskEntry('S1', minAfterNow(2 * 60), { timeEstimate: h(1) }),
  //       // NEXT DAY AT 10
  //       fakePlannedTaskEntry('S2', minAfterNow(34 * 60), { timeEstimate: h(0.5) }),
  //     ],
  //     [fakeRepeatCfg('R1', '01:00', { defaultEstimate: h(1) })],
  //     [
  //       fakeRepeatCfg('R1', undefined, {
  //         defaultEstimate: h(2),
  //         lastTaskCreation: N + 60000,
  //       }),
  //     ],
  //     [],
  //     null,
  //     {
  //       // eslint-disable-next-line @typescript-eslint/naming-convention
  //       '1970-01-02': [
  //         fakeTaskEntry('FD1', { timeEstimate: h(4) }),
  //         fakeTaskEntry('FD2', { timeEstimate: h(2) }),
  //       ],
  //       // eslint-disable-next-line @typescript-eslint/naming-convention
  //       '1970-01-04': [
  //         fakeTaskEntry('FD3', { timeEstimate: h(1) }),
  //         fakeTaskEntry('FD4', { timeEstimate: h(0.5) }),
  //       ],
  //     },
  //     {
  //       startTime: '9:00',
  //       endTime: '17:00',
  //     },
  //     {
  //       startTime: '12:00',
  //       endTime: '13:00',
  //     },
  //   );
  //
  //   expect(r[0]).toEqual({
  //     beyondBudgetTasks: [],
  //     dayDate: '1970-01-01',
  //     entries: [
  //       {
  //         data: jasmine.any(Object),
  //         id: 'S1',
  //         start: h(2),
  //         duration: h(1),
  //         type: 'ScheduledTask',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'N1',
  //         start: hTz(9),
  //         duration: h(2),
  //         type: 'Task',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'LUNCH_BREAK_1970-01-01',
  //         start: hTz(12),
  //         duration: h(1),
  //         type: 'LunchBreak',
  //       },
  //     ],
  //     isToday: true,
  //   } as any);
  //
  //   expect(r[1]).toEqual({
  //     beyondBudgetTasks: [
  //       { id: 'FD2', subTaskIds: [], tagIds: [], timeEstimate: h(2), timeSpent: 0 },
  //     ],
  //     dayDate: '1970-01-02',
  //     entries: [
  //       {
  //         data: jasmine.any(Object),
  //         duration: h(1),
  //         id: 'R1_1970-01-02',
  //         start: dhTz(1, 1),
  //         type: 'ScheduledRepeatProjection',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         duration: h(2),
  //         id: 'R1_1970-01-02',
  //         start: 115200000,
  //         type: 'RepeatProjection',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'S2',
  //         start: dhTz(1, 11),
  //         duration: h(0.5),
  //         type: 'ScheduledTask',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'FD1',
  //         start: dhTz(1, 11.5),
  //         duration: h(0.5),
  //         type: 'SplitTaskPlannedForDay',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'LUNCH_BREAK_1970-01-02',
  //         start: dhTz(1, 12),
  //         duration: h(1),
  //         type: 'LunchBreak',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'FD1__0',
  //         start: dhTz(1, 13),
  //         duration: h(3.5),
  //         type: 'SplitTaskContinuedLast',
  //       },
  //     ],
  //     isToday: false,
  //   } as any);
  //
  //   expect(r[2]).toEqual({
  //     beyondBudgetTasks: [],
  //     dayDate: '1970-01-03',
  //     entries: [
  //       {
  //         data: jasmine.any(Object),
  //         duration: 3600000,
  //         id: 'R1_1970-01-03',
  //         start: 172800000,
  //         type: 'ScheduledRepeatProjection',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'R1_1970-01-03',
  //         start: 201600000,
  //         duration: 7200000,
  //         type: 'RepeatProjection',
  //       },
  //       {
  //         data: { endTime: '13:00', startTime: '12:00' },
  //         id: 'LUNCH_BREAK_1970-01-03',
  //         start: 212400000,
  //         duration: 3600000,
  //         type: 'LunchBreak',
  //       },
  //     ],
  //     isToday: false,
  //   } as any);
  //   expect(r[3]).toEqual({
  //     beyondBudgetTasks: [],
  //     dayDate: '1970-01-04',
  //     entries: [
  //       {
  //         data: jasmine.any(Object),
  //         duration: 3600000,
  //         id: 'R1_1970-01-04',
  //         start: 259200000,
  //         type: 'ScheduledRepeatProjection',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'R1_1970-01-04',
  //         start: 288000000,
  //         duration: h(2),
  //         type: 'RepeatProjection',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'FD3',
  //         start: 295200000,
  //         duration: 3600000,
  //         type: 'TaskPlannedForDay',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'LUNCH_BREAK_1970-01-04',
  //         start: 298800000,
  //         duration: 3600000,
  //         type: 'LunchBreak',
  //       },
  //       {
  //         data: jasmine.any(Object),
  //         id: 'FD4',
  //         start: 302400000,
  //         duration: 1800000,
  //         type: 'TaskPlannedForDay',
  //       },
  //     ],
  //     isToday: false,
  //   } as any);
  // });
});
