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

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

  describe('isValid() - Basic Date Validation', () => {
    it('should validate normal dates', () => {
      const validDate = new Date(2024, 5, 15);
      expect(dateService.isValid(validDate)).toBe(true);
    });

    it('should reject invalid date strings', () => {
      const invalidDate = new Date('not a date');
      expect(dateService.isValid(invalidDate)).toBe(false);
    });

    it('should reject NaN dates', () => {
      const nanDate = new Date(NaN);
      expect(dateService.isValid(nanDate)).toBe(false);
    });

    it('should reject dates created from invalid input', () => {
      const invalidDate = new Date('2024-13-45'); // Invalid month and day
      expect(dateService.isValid(invalidDate)).toBe(false);
    });

    it('should validate leap year dates', () => {
      const leapDay = new Date(2024, 1, 29); // Feb 29, 2024
      expect(dateService.isValid(leapDay)).toBe(true);
    });

    it('should detect invalid leap year dates', () => {
      const invalidLeapDay = new Date(2023, 1, 29); // Feb 29, 2023 (not leap year)
      // JavaScript auto-corrects this to March 1
      expect(invalidLeapDay.getMonth()).toBe(2); // March
      expect(invalidLeapDay.getDate()).toBe(1);
    });
  });

  describe('isWithinBounds() - Date Range Validation', () => {
    it('should accept dates within bounds (1900-2100)', () => {
      const dates = [
        new Date(1900, 0, 1),   // Min bound
        new Date(1950, 6, 15),
        new Date(2000, 0, 1),
        new Date(2024, 5, 15),
        new Date(2100, 11, 31)  // Max bound
      ];

      dates.forEach(date => {
        expect(dateService.isWithinBounds(date)).toBe(true);
      });
    });

    it('should reject dates before 1900', () => {
      const tooEarly = [
        new Date(1899, 11, 31),
        new Date(1800, 0, 1),
        new Date(1000, 6, 15)
      ];

      tooEarly.forEach(date => {
        expect(dateService.isWithinBounds(date)).toBe(false);
      });
    });

    it('should reject dates after 2100', () => {
      const tooLate = [
        new Date(2101, 0, 1),
        new Date(2200, 6, 15),
        new Date(3000, 0, 1)
      ];

      tooLate.forEach(date => {
        expect(dateService.isWithinBounds(date)).toBe(false);
      });
    });

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

    it('should handle boundary dates exactly', () => {
      const minDate = new Date(1900, 0, 1, 0, 0, 0);
      const maxDate = new Date(2100, 11, 31, 23, 59, 59);

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

  describe('isValidRange() - Date Range Validation', () => {
    it('should validate correct date ranges', () => {
      const start = new Date(2024, 0, 15);
      const end = new Date(2024, 0, 20);

      expect(dateService.isValidRange(start, end)).toBe(true);
    });

    it('should accept equal start and end dates', () => {
      const date = new Date(2024, 0, 15, 10, 0);

      expect(dateService.isValidRange(date, date)).toBe(true);
    });

    it('should reject reversed date ranges', () => {
      const start = new Date(2024, 0, 20);
      const end = new Date(2024, 0, 15);

      expect(dateService.isValidRange(start, end)).toBe(false);
    });

    it('should reject ranges with invalid start date', () => {
      const invalidStart = new Date('invalid');
      const validEnd = new Date(2024, 0, 20);

      expect(dateService.isValidRange(invalidStart, validEnd)).toBe(false);
    });

    it('should reject ranges with invalid end date', () => {
      const validStart = new Date(2024, 0, 15);
      const invalidEnd = new Date('invalid');

      expect(dateService.isValidRange(validStart, invalidEnd)).toBe(false);
    });

    it('should validate ranges across year boundaries', () => {
      const start = new Date(2024, 11, 30);
      const end = new Date(2025, 0, 5);

      expect(dateService.isValidRange(start, end)).toBe(true);
    });

    it('should validate multi-year ranges', () => {
      const start = new Date(2020, 0, 1);
      const end = new Date(2024, 11, 31);

      expect(dateService.isValidRange(start, end)).toBe(true);
    });
  });

  describe('validateDate() - Comprehensive Validation', () => {
    it('should validate normal dates without options', () => {
      const date = new Date(2024, 5, 15);
      const result = dateService.validateDate(date);

      expect(result.valid).toBe(true);
      expect(result.error).toBeUndefined();
    });

    it('should reject invalid dates', () => {
      const invalidDate = new Date('invalid');
      const result = dateService.validateDate(invalidDate);

      expect(result.valid).toBe(false);
      expect(result.error).toBe('Invalid date');
    });

    it('should reject out-of-bounds dates', () => {
      const tooEarly = new Date(1899, 0, 1);
      const result = dateService.validateDate(tooEarly);

      expect(result.valid).toBe(false);
      expect(result.error).toContain('out of bounds');
    });

    it('should validate future dates with requireFuture option', () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 10);

      const result = dateService.validateDate(futureDate, { requireFuture: true });

      expect(result.valid).toBe(true);
    });

    it('should reject past dates with requireFuture option', () => {
      const pastDate = new Date();
      pastDate.setDate(pastDate.getDate() - 10);

      const result = dateService.validateDate(pastDate, { requireFuture: true });

      expect(result.valid).toBe(false);
      expect(result.error).toContain('future');
    });

    it('should validate past dates with requirePast option', () => {
      const pastDate = new Date();
      pastDate.setDate(pastDate.getDate() - 10);

      const result = dateService.validateDate(pastDate, { requirePast: true });

      expect(result.valid).toBe(true);
    });

    it('should reject future dates with requirePast option', () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 10);

      const result = dateService.validateDate(futureDate, { requirePast: true });

      expect(result.valid).toBe(false);
      expect(result.error).toContain('past');
    });

    it('should validate dates with minDate constraint', () => {
      const minDate = new Date(2024, 0, 1);
      const testDate = new Date(2024, 6, 15);

      const result = dateService.validateDate(testDate, { minDate });

      expect(result.valid).toBe(true);
    });

    it('should reject dates before minDate', () => {
      const minDate = new Date(2024, 6, 1);
      const testDate = new Date(2024, 5, 15);

      const result = dateService.validateDate(testDate, { minDate });

      expect(result.valid).toBe(false);
      expect(result.error).toContain('after');
    });

    it('should validate dates with maxDate constraint', () => {
      const maxDate = new Date(2024, 11, 31);
      const testDate = new Date(2024, 6, 15);

      const result = dateService.validateDate(testDate, { maxDate });

      expect(result.valid).toBe(true);
    });

    it('should reject dates after maxDate', () => {
      const maxDate = new Date(2024, 6, 31);
      const testDate = new Date(2024, 7, 15);

      const result = dateService.validateDate(testDate, { maxDate });

      expect(result.valid).toBe(false);
      expect(result.error).toContain('before');
    });

    it('should validate dates with both minDate and maxDate', () => {
      const minDate = new Date(2024, 0, 1);
      const maxDate = new Date(2024, 11, 31);
      const testDate = new Date(2024, 6, 15);

      const result = dateService.validateDate(testDate, { minDate, maxDate });

      expect(result.valid).toBe(true);
    });

    it('should reject dates outside min/max range', () => {
      const minDate = new Date(2024, 6, 1);
      const maxDate = new Date(2024, 6, 31);
      const testDate = new Date(2024, 7, 15);

      const result = dateService.validateDate(testDate, { minDate, maxDate });

      expect(result.valid).toBe(false);
    });
  });

  describe('Invalid Date Scenarios', () => {
    it('should handle February 30 (auto-corrects to March)', () => {
      const invalidDate = new Date(2024, 1, 30); // Tries Feb 30, 2024

      // JavaScript auto-corrects to March
      expect(invalidDate.getMonth()).toBe(2); // March
      expect(invalidDate.getDate()).toBe(1);
    });

    it('should handle month overflow (month 13)', () => {
      const date = new Date(2024, 12, 1); // Month 13 = January next year

      expect(date.getFullYear()).toBe(2025);
      expect(date.getMonth()).toBe(0); // January
    });

    it('should handle negative months', () => {
      const date = new Date(2024, -1, 1); // Month -1 = December previous year

      expect(date.getFullYear()).toBe(2023);
      expect(date.getMonth()).toBe(11); // December
    });

    it('should handle day 0 (last day of previous month)', () => {
      const date = new Date(2024, 1, 0); // Day 0 of Feb = Last day of Jan

      expect(date.getMonth()).toBe(0); // January
      expect(date.getDate()).toBe(31);
    });

    it('should handle negative days', () => {
      const date = new Date(2024, 1, -1); // Day -1 of Feb

      expect(date.getMonth()).toBe(0); // January
      expect(date.getDate()).toBe(30);
    });
  });

  describe('Timezone-aware Validation', () => {
    it('should validate UTC dates converted to local timezone', () => {
      const utcString = '2024-06-15T12:00:00Z';
      const localDate = dateService.fromUTC(utcString);

      expect(dateService.isValid(localDate)).toBe(true);
      expect(dateService.isWithinBounds(localDate)).toBe(true);
    });

    it('should maintain validation across timezone conversion', () => {
      const localDate = new Date(2024, 6, 15, 12, 0);
      const utcString = dateService.toUTC(localDate);
      const convertedBack = dateService.fromUTC(utcString);

      expect(dateService.isValid(convertedBack)).toBe(true);

      // Should be same day (accounting for timezone)
      const validation = dateService.validateDate(convertedBack);
      expect(validation.valid).toBe(true);
    });

    it('should validate dates during DST transitions', () => {
      // Spring DST: March 31, 2024 in Copenhagen
      const dstDate = new Date(2024, 2, 31, 2, 30); // Non-existent hour

      // JavaScript handles this, should still be valid
      expect(dateService.isValid(dstDate)).toBe(true);
    });
  });

  describe('Edge Case Validation Combinations', () => {
    it('should reject invalid date even with lenient options', () => {
      const invalidDate = new Date('completely invalid');
      const result = dateService.validateDate(invalidDate, {
        minDate: new Date(1900, 0, 1),
        maxDate: new Date(2100, 11, 31)
      });

      expect(result.valid).toBe(false);
      expect(result.error).toBe('Invalid date');
    });

    it('should validate boundary dates with constraints', () => {
      const boundaryDate = new Date(1900, 0, 1);
      const result = dateService.validateDate(boundaryDate, {
        minDate: new Date(1900, 0, 1)
      });

      expect(result.valid).toBe(true);
    });

    it('should provide meaningful error messages', () => {
      const testCases = [
        { date: new Date('invalid'), expectedError: 'Invalid date' },
        { date: new Date(1800, 0, 1), expectedError: 'bounds' },
      ];

      testCases.forEach(({ date, expectedError }) => {
        const result = dateService.validateDate(date);
        expect(result.valid).toBe(false);
        expect(result.error).toContain(expectedError);
      });
    });

    it('should validate leap year boundaries correctly', () => {
      const leapYearEnd = new Date(2024, 1, 29); // Last day of Feb in leap year
      const nonLeapYearEnd = new Date(2023, 1, 28); // Last day of Feb in non-leap year

      expect(dateService.validateDate(leapYearEnd).valid).toBe(true);
      expect(dateService.validateDate(nonLeapYearEnd).valid).toBe(true);
    });
  });
});
