import { getNewestPossibleDueDate } from './get-newest-possible-due-date.util';
import { DEFAULT_TASK_REPEAT_CFG, TaskRepeatCfg } from '../task-repeat-cfg.model';
import { getDbDateStr } from '../../../util/get-db-date-str';
import { Log } from '../../../core/log';
import { dateStrToUtcDate } from '../../../util/date-str-to-utc-date';

/* eslint-disable no-mixed-operators */

const FAKE_MONDAY_THE_10TH = new Date(2022, 0, 10).getTime();

const DUMMY_REPEATABLE_TASK: TaskRepeatCfg = {
  ...DEFAULT_TASK_REPEAT_CFG,
  id: 'REPEATABLE_DEFAULT',
  title: 'REPEATABLE_DEFAULT',
  quickSetting: 'DAILY',
  lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH),
  defaultEstimate: undefined,
  projectId: null,
  startTime: undefined,
  remindAt: undefined,
  isPaused: false,
  repeatCycle: 'WEEKLY',
  startDate: getDbDateStr(FAKE_MONDAY_THE_10TH),
  repeatEvery: 1,
  monday: false,
  tuesday: false,
  wednesday: false,
  thursday: false,
  friday: false,
  saturday: false,
  sunday: false,
  tagIds: [],
  order: 0,
};

const HOUR = 60 * 60 * 1000;
const DAY = 24 * HOUR;

const dummyRepeatable = (id: string, fields: Partial<TaskRepeatCfg>): TaskRepeatCfg => ({
  ...DUMMY_REPEATABLE_TASK,
  id,
  ...fields,
});

const testCase = (
  cfg: TaskRepeatCfg,
  today: Date,
  startDate: Date | number,
  expected: Date | null,
): void => {
  if (expected) {
    expected.setHours(12, 0, 0, 0);
  }

  expect(
    getNewestPossibleDueDate(
      {
        ...cfg,
        startDate: getDbDateStr(startDate),
      },
      today,
    ),
  ).toEqual(expected);
};

describe('getNewestPossibleDueDate()', () => {
  describe('Input validation', () => {
    it('should use fallback date (1970-01-01) when startDate is not defined', () => {
      const cfg = dummyRepeatable('ID1', {
        startDate: undefined as any,
        repeatCycle: 'DAILY',
        repeatEvery: 1,
        lastTaskCreationDay: '1970-01-01',
      });
      const today = new Date(2022, 0, 10);
      // Should not throw, and should return a valid date using the fallback
      const result = getNewestPossibleDueDate(cfg, today);
      expect(result).toBeTruthy();
    });

    it('should return null and warn if repeatEvery is not a positive integer', () => {
      spyOn(Log, 'warn');

      const cfg1 = dummyRepeatable('ID1', {
        repeatEvery: 0,
      });
      expect(getNewestPossibleDueDate(cfg1, new Date())).toBeNull();

      const cfg2 = dummyRepeatable('ID1', {
        repeatEvery: -1,
      });
      expect(getNewestPossibleDueDate(cfg2, new Date())).toBeNull();

      const cfg3 = dummyRepeatable('ID1', {
        repeatEvery: 1.5,
      });
      expect(getNewestPossibleDueDate(cfg3, new Date())).toBeNull();

      expect(Log.warn).toHaveBeenCalledTimes(3);
    });
  });

  describe('DAILY', () => {
    const testCases = [
      {
        description: 'should return today date if today is due day',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
        }),
        today: new Date(2022, 1, 11),
        startDate: new Date(2022, 0, 10),
        expectedDate: new Date(2022, 1, 11),
      },
      {
        description: 'should return date if today is after due',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 14).getTime()),
        }),
        startDate: new Date(2022, 0, 14),
        today: dateStrToUtcDate('2022-03-14'),
        expectedDate: dateStrToUtcDate('2022-03-14'),
      },
      {
        description: 'should work for repeat every 1',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 3,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 14).getTime()),
        }),
        startDate: new Date(2022, 0, 14),
        today: dateStrToUtcDate('2022-03-14'),
        expectedDate: dateStrToUtcDate('2022-03-12'),
      },
      {
        description: 'should return null if today and already created',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 14).getTime()),
        }),
        today: new Date(2022, 0, 14),
        startDate: new Date(2022, 0, 14),
        expectedDate: null,
      },
      {
        description: 'should return null if last applicable is already created',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 5,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 15).getTime()),
        }),
        today: new Date(2022, 0, 17),
        startDate: new Date(2022, 0, 10),
        expectedDate: null,
      },
    ];

    testCases.forEach(
      ({ description, startDate, taskRepeatCfg, today, expectedDate }) => {
        it(description, () => {
          testCase(taskRepeatCfg, today, startDate, expectedDate);
        });
      },
    );
  });

  describe('WEEKLY', () => {
    const testCases = [
      {
        // 1-10-22 is a monday
        description: 'should return today date if today is due day',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
          monday: true,
        }),
        today: new Date(2022, 0, 17),
        startDate: new Date(2022, 0, 10),
        expectedDate: new Date(2022, 0, 17),
      },
      {
        // 1-10-22 is a monday
        description: 'should return null if no weekday is set',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
        }),
        today: new Date(2022, 0, 17),
        startDate: new Date(2022, 0, 10),
        expectedDate: null,
      },
      {
        description: 'should work also if there is a weird lastTaskCreation date',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 16).getTime()),
          monday: true,
        }),
        today: new Date(2022, 0, 17),
        startDate: new Date(2022, 0, 10),
        expectedDate: new Date(2022, 0, 17),
      },
      {
        description: 'should return date for the proper weekday',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 7).getTime()),
          friday: true,
        }),
        // is a friday
        startDate: new Date(2022, 0, 7),
        today: dateStrToUtcDate('2022-03-24'),
        // is a friday
        expectedDate: dateStrToUtcDate('2022-03-18'),
      },
      {
        description: 'should work for repeat every 1',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 2,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 3).getTime()),
          monday: true,
        }),
        // monday
        startDate: new Date(2022, 0, 3),
        today: new Date(2022, 0, 24),
        expectedDate: new Date(2022, 0, 17),
      },
      {
        description: 'should work for repeat every 2',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 2,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 3).getTime()),
          monday: true,
          tuesday: true,
        }),
        // monday
        startDate: new Date(2022, 0, 3),
        today: new Date(2022, 0, 24),
        expectedDate: new Date(2022, 0, 18),
      },
      {
        description: 'should return null if today and already created',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
          monday: true,
        }),
        today: new Date(2022, 0, 10),
        startDate: new Date(2022, 0, 10),
        expectedDate: null,
      },
      {
        description: 'should return null if last applicable is already created',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 2,
          monday: true,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 17).getTime()),
        }),
        today: dateStrToUtcDate('2022-01-27'),
        startDate: new Date(2022, 0, 3),
        expectedDate: null,
      },
      {
        description: 'should return last week if last week was due and not set',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: '1970-01-01',
          monday: true,
        }),
        today: new Date(FAKE_MONDAY_THE_10TH - DAY),
        startDate: new Date(FAKE_MONDAY_THE_10TH - DAY * 14),
        expectedDate: new Date(FAKE_MONDAY_THE_10TH - DAY * 7),
      },
      {
        description:
          'should return last week if last week was due and last creation is long ago',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 17).getTime()), // Jan 17, 2022 (Monday)
          monday: true,
        }),
        startDate: new Date(2022, 0, 10), // Jan 10, 2022 (Monday)
        today: new Date(2022, 7, 4), // Aug 4, 2022 (Thursday)
        expectedDate: new Date(2022, 7, 1), // Aug 1, 2022 (Monday)
      },
      {
        description: 'should return NULL if not applicable',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 5,
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH + DAY * (7 * 5 + 0)),
          monday: true,
        }),
        startDate: new Date(FAKE_MONDAY_THE_10TH),
        today: new Date(FAKE_MONDAY_THE_10TH + DAY * (5 * 7 + 7)),
        expectedDate: null,
      },
      {
        description: 'should return NULL if start date is in the future',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH - DAY * 7),
          monday: true,
        }),
        startDate: new Date(FAKE_MONDAY_THE_10TH),
        today: new Date(FAKE_MONDAY_THE_10TH - 1 * DAY),
        expectedDate: null,
      },
      {
        description: 'should respect start date is in the future',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH - DAY * 7),
          monday: true,
          tuesday: true,
          wednesday: true,
        }),
        startDate: new Date(FAKE_MONDAY_THE_10TH + 4 * DAY),
        today: new Date(FAKE_MONDAY_THE_10TH + 1 * DAY),
        expectedDate: null,
      },
    ];

    testCases.forEach(
      ({ description, taskRepeatCfg, startDate, today, expectedDate }) => {
        it(description, () => {
          testCase(taskRepeatCfg, today, startDate, expectedDate);
        });
      },
    );
  });

  describe('Biweekly with weekdays (#6298)', () => {
    // Issue #6298: "Every 2 weeks Mon-Fri" starting Monday Feb 2, 2026
    const START_DATE = new Date(2026, 1, 2); // Monday Feb 2, 2026

    const biweeklyCfg = (lastCreationDay: string): TaskRepeatCfg =>
      dummyRepeatable('BIWEEKLY', {
        repeatCycle: 'WEEKLY',
        repeatEvery: 2,
        monday: true,
        tuesday: true,
        wednesday: true,
        thursday: true,
        friday: true,
        lastTaskCreationDay: lastCreationDay,
      });

    it('should find Friday of week 0 when searching backward from that Friday', () => {
      // Friday Feb 6 is in week 0 (active) — backward search should find it
      const cfg = biweeklyCfg('1970-01-01');
      testCase(cfg, new Date(2026, 1, 6), START_DATE, new Date(2026, 1, 6)); // Fri Feb 6
    });

    it('should NOT find Friday of week 1 (off-week)', () => {
      // Friday Feb 13 is in week 1 (off) — backward search should find Thu Feb 5 at most
      // but since last creation is already Feb 5, it returns null
      const cfg = biweeklyCfg(getDbDateStr(new Date(2026, 1, 6)));
      testCase(cfg, new Date(2026, 1, 13), START_DATE, null);
    });

    it('should find Monday of week 2 when today is that Monday', () => {
      // Monday Feb 16 is in week 2 (active)
      const cfg = biweeklyCfg(getDbDateStr(new Date(2026, 1, 6)));
      testCase(cfg, new Date(2026, 1, 16), START_DATE, new Date(2026, 1, 16));
    });
  });

  describe('MONTHLY', () => {
    const testCases = [
      {
        description: 'should return today date if today is due day',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
        }),
        today: new Date(2022, 1, 10),
        startDate: new Date(2022, 0, 10),
        expectedDate: new Date(2022, 1, 10),
      },
      {
        description: 'should return date if today is after due',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 3,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 14).getTime()),
        }),
        today: dateStrToUtcDate('2022-08-14'),
        startDate: new Date(2022, 0, 14),
        expectedDate: dateStrToUtcDate('2022-07-14'),
      },
      {
        description: 'should return null if last applicable is already created',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 3,
          lastTaskCreationDay: '2022-07-14',
        }),
        today: dateStrToUtcDate('2022-08-14'),
        startDate: new Date(2022, 0, 14),
        expectedDate: null,
      },
      {
        description: "should not return values for marco's edge case",
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: '2024-06-26',
        }),
        today: dateStrToUtcDate('2024-07-16'),
        startDate: new Date(2024, 0, 26),
        expectedDate: null,
      },
      {
        description: 'should handle month-end dates correctly (31st to February)',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: '2022-01-31',
        }),
        today: dateStrToUtcDate('2022-03-01'),
        startDate: dateStrToUtcDate('2022-01-31'),
        expectedDate: dateStrToUtcDate('2022-02-28'),
      },
      {
        description: 'should handle month-end dates correctly in leap year',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: '2020-01-31',
        }),
        today: dateStrToUtcDate('2020-03-01'),
        startDate: dateStrToUtcDate('2020-01-31'),
        expectedDate: dateStrToUtcDate('2020-02-29'),
      },
      {
        description: 'should handle 30th correctly for months with fewer days',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: '2022-01-30',
        }),
        today: dateStrToUtcDate('2022-03-01'),
        startDate: dateStrToUtcDate('2022-01-30'),
        expectedDate: dateStrToUtcDate('2022-02-28'),
      },
      {
        description: 'should handle 31st across multiple months',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: '2022-01-31',
        }),
        today: dateStrToUtcDate('2022-05-01'),
        startDate: dateStrToUtcDate('2022-01-31'),
        expectedDate: dateStrToUtcDate('2022-04-30'),
      },
    ];

    testCases.forEach(
      ({ description, startDate, taskRepeatCfg, today, expectedDate }) => {
        it(description, () => {
          if (expectedDate) {
            expectedDate.setHours(0, 0, 0, 0);
          }
          testCase(taskRepeatCfg, today, startDate, expectedDate);
        });
      },
    );
  });

  describe('MONTHLY monthlyLastDay (issue #7726)', () => {
    it('returns the last day of the current month', () => {
      const cfg = dummyRepeatable('ID1', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyLastDay: true,
        lastTaskCreationDay: '2026-05-31',
      });
      testCase(cfg, new Date(2026, 5, 30), new Date(2026, 4, 31), new Date(2026, 5, 30));
    });

    it('clamps to month-end even when startDate day-of-month is 30', () => {
      // A recurrence set up in a 30-day month must still hit 31 in long
      // months — the anchor is decoupled from startDate's day.
      const cfg = dummyRepeatable('ID1', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyLastDay: true,
        lastTaskCreationDay: '2026-06-30',
      });
      testCase(cfg, new Date(2026, 6, 31), new Date(2026, 5, 30), new Date(2026, 6, 31));
    });

    it('returns null when the latest month-end is already created', () => {
      const cfg = dummyRepeatable('ID1', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyLastDay: true,
        lastTaskCreationDay: '2026-06-30',
      });
      testCase(cfg, new Date(2026, 6, 15), new Date(2026, 4, 31), null);
    });
  });

  describe('MONTHLY Nth weekday (issue #6040)', () => {
    it('returns the Nth weekday of this month when today equals it', () => {
      // 1st Thursday of Jan 2026 = Jan 1 (Thu). today = Jan 1.
      const today = new Date(2026, 0, 1);
      const cfg = dummyRepeatable('ID-NTH-NEWEST-1', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyWeekOfMonth: 1,
        monthlyWeekday: 4,
        lastTaskCreationDay: '2025-12-04', // 1st Thu of Dec 2025
      });
      testCase(cfg, today, new Date(2025, 11, 4), new Date(2026, 0, 1));
    });

    it('returns null when the candidate equals lastTaskCreation (uses > not >=)', () => {
      // 1st Thursday of Jan 2026 = Jan 1. lastTaskCreation = Jan 1.
      // today = Jan 1 → no newer occurrence to surface.
      const today = new Date(2026, 0, 1);
      const cfg = dummyRepeatable('ID-NTH-NEWEST-2', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyWeekOfMonth: 1,
        monthlyWeekday: 4,
        lastTaskCreationDay: getDbDateStr(new Date(2026, 0, 1)),
      });
      testCase(cfg, today, new Date(2025, 11, 4), null);
    });

    it('skips off-cycle months when repeatEvery=2', () => {
      // startDate = Jan 1 2026 (1st Thu). repeatEvery=2 → expect Jan, Mar, May…
      // today = Feb 28 2026; Feb is off-cycle, so newest valid is Jan's 1st Thu.
      const today = new Date(2026, 1, 28);
      const cfg = dummyRepeatable('ID-NTH-NEWEST-3', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 2,
        monthlyWeekOfMonth: 1,
        monthlyWeekday: 4,
        lastTaskCreationDay: '1970-01-01',
      });
      testCase(cfg, today, new Date(2026, 0, 1), new Date(2026, 0, 1));
    });

    it('falls back to legacy behavior when anchors are out of range', () => {
      // monthlyWeekOfMonth=99 should not engage the NTH branch — the cfg
      // behaves like a plain day-of-month MONTHLY anchored to startDate.
      const today = new Date(2022, 1, 15);
      const cfg = dummyRepeatable('ID-NTH-NEWEST-4', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyWeekOfMonth: 99 as never,
        monthlyWeekday: 1,
        lastTaskCreationDay: '1970-01-01',
      });
      testCase(cfg, today, new Date(2022, 0, 15), new Date(2022, 1, 15));
    });

    it('falls back to legacy behavior when monthlyWeekOfMonth is null (form sentinel)', () => {
      // The form's "(Day of month)" option stores null transiently. Even if
      // a stray null reaches the calc layer (e.g. a pre-normalization save
      // path), the gatekeeper rejects it and behavior is day-of-month.
      const today = new Date(2022, 1, 15);
      const cfg = dummyRepeatable('ID-NTH-NEWEST-5', {
        repeatCycle: 'MONTHLY',
        repeatEvery: 1,
        monthlyWeekOfMonth: null as never,
        monthlyWeekday: 1,
        lastTaskCreationDay: '1970-01-01',
      });
      testCase(cfg, today, new Date(2022, 0, 15), new Date(2022, 1, 15));
    });
  });

  describe('Timezone Edge Cases', () => {
    describe('DST transitions', () => {
      it('should handle spring DST transition (clocks forward)', () => {
        // March 13, 2022 is when DST starts in US (2 AM becomes 3 AM)
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2022-03-12').getTime()),
        });
        const today = dateStrToUtcDate('2022-03-14');
        const startDate = dateStrToUtcDate('2022-03-12');
        const expected = dateStrToUtcDate('2022-03-14');
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });

      it('should handle fall DST transition (clocks back)', () => {
        // November 6, 2022 is when DST ends in US (2 AM becomes 1 AM)
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2022-11-05').getTime()),
        });
        const today = dateStrToUtcDate('2022-11-07');
        const startDate = dateStrToUtcDate('2022-11-05');
        const expected = dateStrToUtcDate('2022-11-07');
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });
    });

    describe('Year boundary crossing', () => {
      it('should handle daily repeat across year boundary', () => {
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2021-12-30').getTime()),
        });
        const today = dateStrToUtcDate('2022-01-02');
        const startDate = dateStrToUtcDate('2021-12-30');
        const expected = dateStrToUtcDate('2022-01-02');
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });

      it('should handle weekly repeat across year boundary', () => {
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2021-12-27').getTime()), // Monday
          monday: true,
        });
        const today = new Date(2022, 0, 3); // Monday
        const startDate = dateStrToUtcDate('2021-12-27');
        const expected = new Date(2022, 0, 3);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });

      it('should handle monthly repeat across year boundary', () => {
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2021-12-15').getTime()),
        });
        const today = new Date(2022, 0, 15);
        const startDate = dateStrToUtcDate('2021-12-15');
        const expected = new Date(2022, 0, 15);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });
    });

    describe('Leap year edge cases', () => {
      it('should handle February 29 in leap year for monthly repeat', () => {
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'MONTHLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2020-01-29').getTime()),
        });
        const today = dateStrToUtcDate('2020-03-01');
        const startDate = dateStrToUtcDate('2020-01-29');
        const expected = dateStrToUtcDate('2020-02-29');
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });

      it('should handle February 29 start date in non-leap year', () => {
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(dateStrToUtcDate('2020-02-29').getTime()),
        });
        const today = dateStrToUtcDate('2021-03-01');
        const startDate = dateStrToUtcDate('2020-02-29');
        // The function doesn't handle leap year edge case for Feb 29 -> Feb 28 conversion
        // It will return March 1st instead since it sets the date directly
        const expected = dateStrToUtcDate('2021-03-01');
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });
    });

    describe('Midnight and near-midnight times', () => {
      it('should handle task created at 23:59:59', () => {
        const lastCreation = new Date(2022, 0, 10, 23, 59, 59, 999);
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(lastCreation.getTime()),
        });
        const today = new Date(2022, 0, 12);
        const startDate = new Date(2022, 0, 10);
        const expected = new Date(2022, 0, 12);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });

      it('should handle task created at 00:00:01', () => {
        const lastCreation = new Date(2022, 0, 11, 0, 0, 1, 0);
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(lastCreation.getTime()),
        });
        const today = new Date(2022, 0, 12);
        const startDate = new Date(2022, 0, 10);
        const expected = new Date(2022, 0, 12);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });
    });

    describe('International Date Line crossing', () => {
      it('should handle dates near International Date Line', () => {
        // Test with a date that would be different days in different timezones
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10, 23, 0, 0).getTime()),
        });
        const today = new Date(2022, 0, 12, 1, 0, 0);
        const startDate = new Date(2022, 0, 10, 12, 0, 0);

        // Test that dates work properly in local timezone
        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        // Since we're working with local dates, result should be Jan 12
        const expected = new Date(2022, 0, 12, 12, 0, 0);
        expect(result).toEqual(expected);
      });
    });

    describe('Multi-timezone scenario simulations', () => {
      it('should handle task created in one timezone and checked in another', () => {
        // Simulate: Task created at 11 PM on Jan 10
        // Then checked on Jan 12
        const lastCreationLA = new Date(2022, 0, 10, 23, 0, 0);
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'DAILY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(lastCreationLA.getTime()),
        });

        const todayBerlin = new Date(2022, 0, 12, 9, 0, 0);
        const startDate = new Date(2022, 0, 10);
        const expected = new Date(2022, 0, 12, 9, 0, 0);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          todayBerlin,
        );
        expect(result).toEqual(expected);
      });

      it('should handle weekly repeat with timezone differences', () => {
        // Task repeats every Monday, created Sunday night
        const lastCreation = new Date(2022, 0, 9, 23, 0, 0); // Sunday 11 PM
        const cfg = dummyRepeatable('ID1', {
          repeatCycle: 'WEEKLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(lastCreation.getTime()),
          monday: true,
        });

        const today = new Date(2022, 0, 17, 10, 0, 0); // Monday 10 AM
        const startDate = new Date(2022, 0, 3); // Previous Monday
        const expected = new Date(2022, 0, 17, 10, 0, 0);
        expected.setHours(12, 0, 0, 0);

        const result = getNewestPossibleDueDate(
          { ...cfg, startDate: getDbDateStr(startDate) },
          today,
        );
        expect(result).toEqual(expected);
      });
    });
  });

  describe('YEARLY', () => {
    const testCases = [
      {
        description: 'should return date if applicable',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH),
        }),
        today: new Date(FAKE_MONDAY_THE_10TH + DAY * 366),
        startDate: FAKE_MONDAY_THE_10TH,
        expectedDate: new Date(FAKE_MONDAY_THE_10TH + DAY * 365),
      },
      {
        description: 'should return null if not applicable',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 2,
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH),
        }),
        today: new Date(FAKE_MONDAY_THE_10TH + DAY * 365),
        startDate: FAKE_MONDAY_THE_10TH,
        expectedDate: null,
      },
      {
        description: 'should return date if applicable 2',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 2,
          lastTaskCreationDay: getDbDateStr(FAKE_MONDAY_THE_10TH),
        }),
        today: new Date(FAKE_MONDAY_THE_10TH + DAY * 365 * 3),
        startDate: FAKE_MONDAY_THE_10TH,
        expectedDate: new Date(FAKE_MONDAY_THE_10TH + DAY * 365 * 2),
      },
      {
        description: 'should return date if applicable 3',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 3,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
        }),
        today: new Date(2026, 0, 10),
        startDate: FAKE_MONDAY_THE_10TH,
        expectedDate: new Date(2025, 0, 10),
      },
      {
        description: 'should return null if NOT applicable',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 2,
          lastTaskCreationDay: getDbDateStr(new Date(2022, 0, 10).getTime()),
        }),
        today: new Date(2023, 0, 10),
        startDate: new Date(2022, 0, 10).getTime(),
        expectedDate: null,
      },
      {
        description: 'should not return values for Marco`s edge case for year',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(new Date(2024, 0, 26).getTime()),
        }),
        today: dateStrToUtcDate('2024-07-16'),
        startDate: dateStrToUtcDate('2023-01-26'),
        expectedDate: null,
      },
      {
        description: 'should not return values for Marco`s edge case for year 2',
        taskRepeatCfg: dummyRepeatable('ID1', {
          repeatCycle: 'YEARLY',
          repeatEvery: 1,
          lastTaskCreationDay: getDbDateStr(new Date(2024, 0, 26).getTime()),
        }),
        today: dateStrToUtcDate('2024-05-27'),
        startDate: dateStrToUtcDate('2023-01-26'),
        expectedDate: null,
      },
    ];

    testCases.forEach(
      ({ description, startDate, taskRepeatCfg, today, expectedDate }) => {
        it(description, () => {
          if (expectedDate) {
            expectedDate.setHours(0, 0, 0, 0);
          }
          testCase(taskRepeatCfg, today, startDate, expectedDate);
        });
      },
    );
  });
});
