import { describe, it, expect } from 'vitest';
import { DateService } from '../../src/utils/DateService';
import { createTestConfig } from '../helpers/config-helpers';

describe('DateService - Edge Cases', () => {
  const config = createTestConfig();
  const dateService = new DateService(config);

  describe('Leap Year Handling', () => {
    it('should handle February 29 in leap year (2024)', () => {
      const leapDate = new Date(2024, 1, 29); // Feb 29, 2024
      expect(dateService.isValid(leapDate)).toBe(true);
      expect(leapDate.getMonth()).toBe(1); // February
      expect(leapDate.getDate()).toBe(29);
    });

    it('should reject February 29 in non-leap year (2023)', () => {
      const invalidDate = new Date(2023, 1, 29); // Tries Feb 29, 2023
      // JavaScript auto-corrects to March 1
      expect(invalidDate.getMonth()).toBe(2); // March
      expect(invalidDate.getDate()).toBe(1);
    });

    it('should handle February 28 in non-leap year', () => {
      const validDate = new Date(2023, 1, 28); // Feb 28, 2023
      expect(dateService.isValid(validDate)).toBe(true);
      expect(validDate.getMonth()).toBe(1); // February
      expect(validDate.getDate()).toBe(28);
    });

    it('should correctly add 1 year to Feb 29 (leap year)', () => {
      const leapDate = new Date(2024, 1, 29);
      const nextYear = dateService.addDays(leapDate, 365); // 2025 is not leap year

      // Should be Feb 28, 2025 (or March 1 depending on implementation)
      expect(nextYear.getFullYear()).toBe(2025);
      expect(nextYear.getMonth()).toBeGreaterThanOrEqual(1); // Feb or March
    });

    it('should validate leap year dates with isWithinBounds', () => {
      const leapDate2024 = new Date(2024, 1, 29);
      const leapDate2000 = new Date(2000, 1, 29);

      expect(dateService.isWithinBounds(leapDate2024)).toBe(true);
      expect(dateService.isWithinBounds(leapDate2000)).toBe(true);
    });
  });

  describe('ISO Week 53 Handling', () => {
    it('should correctly identify week 53 in 2020 (has week 53)', () => {
      const dec31_2020 = new Date(2020, 11, 31); // Dec 31, 2020
      const weekNum = dateService.getWeekNumber(dec31_2020);
      expect(weekNum).toBe(53);
    });

    it('should correctly identify week 53 in 2026 (has week 53)', () => {
      const dec31_2026 = new Date(2026, 11, 31); // Dec 31, 2026
      const weekNum = dateService.getWeekNumber(dec31_2026);
      expect(weekNum).toBe(53);
    });

    it('should NOT have week 53 in 2021 (goes to week 52)', () => {
      const dec31_2021 = new Date(2021, 11, 31); // Dec 31, 2021
      const weekNum = dateService.getWeekNumber(dec31_2021);
      expect(weekNum).toBe(52);
    });

    it('should handle transition from week 53 to week 1', () => {
      const lastDayOf2020 = new Date(2020, 11, 31); // Week 53
      const firstDayOf2021 = dateService.addDays(lastDayOf2020, 1);

      expect(dateService.getWeekNumber(lastDayOf2020)).toBe(53);
      expect(dateService.getWeekNumber(firstDayOf2021)).toBe(53); // Still week 53!

      // Monday after should be week 1
      const firstMonday2021 = new Date(2021, 0, 4);
      expect(dateService.getWeekNumber(firstMonday2021)).toBe(1);
    });

    it('should get correct week bounds for week 53', () => {
      const dec31_2020 = new Date(2020, 11, 31);
      const weekBounds = dateService.getWeekBounds(dec31_2020);

      // Week 53 of 2020 starts on Monday Dec 28, 2020
      expect(weekBounds.start.getDate()).toBe(28);
      expect(weekBounds.start.getMonth()).toBe(11); // December

      // Ends on Sunday Jan 3, 2021
      expect(weekBounds.end.getDate()).toBe(3);
      expect(weekBounds.end.getMonth()).toBe(0); // January
      expect(weekBounds.end.getFullYear()).toBe(2021);
    });
  });

  describe('Month Boundary Edge Cases', () => {
    it('should correctly add months across year boundary', () => {
      const nov2024 = new Date(2024, 10, 15); // Nov 15, 2024
      const feb2025 = dateService.addMonths(nov2024, 3);

      expect(feb2025.getFullYear()).toBe(2025);
      expect(feb2025.getMonth()).toBe(1); // February
      expect(feb2025.getDate()).toBe(15);
    });

    it('should handle month-end overflow (Jan 31 + 1 month)', () => {
      const jan31 = new Date(2024, 0, 31);
      const result = dateService.addMonths(jan31, 1);

      // date-fns addMonths handles this gracefully
      expect(result.getMonth()).toBe(1); // February
      expect(result.getFullYear()).toBe(2024);
      // Will be Feb 29 (leap year) or last day of Feb
    });

    it('should handle adding negative months', () => {
      const mar2024 = new Date(2024, 2, 15); // March 15, 2024
      const dec2023 = dateService.addMonths(mar2024, -3);

      expect(dec2023.getFullYear()).toBe(2023);
      expect(dec2023.getMonth()).toBe(11); // December
      expect(dec2023.getDate()).toBe(15);
    });
  });

  describe('Year Boundary Edge Cases', () => {
    it('should handle year transition (Dec 31 -> Jan 1)', () => {
      const dec31 = new Date(2024, 11, 31);
      const jan1 = dateService.addDays(dec31, 1);

      expect(jan1.getFullYear()).toBe(2025);
      expect(jan1.getMonth()).toBe(0); // January
      expect(jan1.getDate()).toBe(1);
    });

    it('should handle reverse year transition (Jan 1 -> Dec 31)', () => {
      const jan1 = new Date(2024, 0, 1);
      const dec31 = dateService.addDays(jan1, -1);

      expect(dec31.getFullYear()).toBe(2023);
      expect(dec31.getMonth()).toBe(11); // December
      expect(dec31.getDate()).toBe(31);
    });

    it('should correctly calculate week bounds at year boundary', () => {
      const jan1_2024 = new Date(2024, 0, 1);
      const weekBounds = dateService.getWeekBounds(jan1_2024);

      // Jan 1, 2024 is a Monday (week 1)
      expect(weekBounds.start.getDate()).toBe(1);
      expect(weekBounds.start.getMonth()).toBe(0);
      expect(weekBounds.start.getFullYear()).toBe(2024);
    });
  });

  describe('DST Transition Edge Cases', () => {
    it('should handle spring DST transition (CET -> CEST)', () => {
      // Last Sunday of March 2024: March 31, 02:00 -> 03:00
      const beforeDST = new Date(2024, 2, 31, 1, 30); // 01:30 CET
      const afterDST = new Date(2024, 2, 31, 3, 30);  // 03:30 CEST

      expect(dateService.isValid(beforeDST)).toBe(true);
      expect(dateService.isValid(afterDST)).toBe(true);

      // The hour 02:00-03:00 doesn't exist!
      const nonExistentTime = new Date(2024, 2, 31, 2, 30);
      // JavaScript auto-adjusts this
      expect(nonExistentTime.getHours()).not.toBe(2);
    });

    it('should handle fall DST transition (CEST -> CET)', () => {
      // Last Sunday of October 2024: October 27, 03:00 -> 02:00
      const beforeDST = new Date(2024, 9, 27, 2, 30, 0, 0);
      const afterDST = new Date(2024, 9, 27, 3, 30, 0, 0);

      expect(dateService.isValid(beforeDST)).toBe(true);
      expect(dateService.isValid(afterDST)).toBe(true);

      // 02:00-03:00 exists TWICE (ambiguous hour)
      // This is handled by timezone-aware libraries
    });

    it('should calculate duration correctly across DST', () => {
      // Event spanning DST transition
      const start = new Date(2024, 2, 31, 1, 0); // Before DST
      const end = new Date(2024, 2, 31, 4, 0);   // After DST

      const duration = dateService.getDurationMinutes(start, end);

      // Clock time: 3 hours, but actual duration: 2 hours (due to DST)
      // date-fns should handle this correctly
      expect(duration).toBeGreaterThan(0);
    });
  });

  describe('Extreme Date Values', () => {
    it('should reject dates before 1900', () => {
      const oldDate = new Date(1899, 11, 31);
      expect(dateService.isWithinBounds(oldDate)).toBe(false);
    });

    it('should reject dates after 2100', () => {
      const futureDate = new Date(2101, 0, 1);
      expect(dateService.isWithinBounds(futureDate)).toBe(false);
    });

    it('should accept boundary dates (1900 and 2100)', () => {
      const minDate = new Date(1900, 0, 1);
      const maxDate = new Date(2100, 11, 31);

      expect(dateService.isWithinBounds(minDate)).toBe(true);
      expect(dateService.isWithinBounds(maxDate)).toBe(true);
    });

    it('should validate invalid Date objects', () => {
      const invalidDate = new Date('invalid');
      expect(dateService.isValid(invalidDate)).toBe(false);
      expect(dateService.isWithinBounds(invalidDate)).toBe(false);
    });
  });
});
