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

describe('DateService', () => {
  let dateService: DateService;

  beforeEach(() => {
    const config = createTestConfig();
    dateService = new DateService(config);
  });

  describe('Core Conversions', () => {
    it('should convert local date to UTC', () => {
      // 2024-01-15 10:00:00 Copenhagen (UTC+1 in winter)
      const localDate = new Date(2024, 0, 15, 10, 0, 0);
      const utcString = dateService.toUTC(localDate);
      
      // Should be 09:00:00 UTC
      expect(utcString).toContain('2024-01-15T09:00:00');
      expect(utcString).toContain('Z');
    });

    it('should convert UTC to local date', () => {
      const utcString = '2024-01-15T09:00:00.000Z';
      const localDate = dateService.fromUTC(utcString);
      
      // Should be 10:00 in Copenhagen (UTC+1)
      expect(localDate.getHours()).toBe(10);
      expect(localDate.getMinutes()).toBe(0);
    });

    it('should handle summer time (DST)', () => {
      // 2024-07-15 10:00:00 Copenhagen (UTC+2 in summer)
      const localDate = new Date(2024, 6, 15, 10, 0, 0);
      const utcString = dateService.toUTC(localDate);
      
      // Should be 08:00:00 UTC
      expect(utcString).toContain('2024-07-15T08:00:00');
    });
  });

  describe('Time Formatting', () => {
    it('should format time without seconds', () => {
      const date = new Date(2024, 0, 15, 14, 30, 45);
      const formatted = dateService.formatTime(date);
      
      expect(formatted).toBe('14:30');
    });

    it('should format time with seconds', () => {
      const date = new Date(2024, 0, 15, 14, 30, 45);
      const formatted = dateService.formatTime(date, true);
      
      expect(formatted).toBe('14:30:45');
    });

    it('should format time range', () => {
      const start = new Date(2024, 0, 15, 9, 0, 0);
      const end = new Date(2024, 0, 15, 10, 30, 0);
      const formatted = dateService.formatTimeRange(start, end);
      
      expect(formatted).toBe('09:00 - 10:30');
    });

    it('should format technical datetime', () => {
      const date = new Date(2024, 0, 15, 14, 30, 45);
      const formatted = dateService.formatTechnicalDateTime(date);
      
      expect(formatted).toBe('2024-01-15 14:30:45');
    });

    it('should format date as ISO', () => {
      const date = new Date(2024, 0, 15, 14, 30, 0);
      const formatted = dateService.formatDate(date);
      
      expect(formatted).toBe('2024-01-15');
    });
  });

  describe('Time Calculations', () => {
    it('should convert time string to minutes', () => {
      expect(dateService.timeToMinutes('09:00')).toBe(540);
      expect(dateService.timeToMinutes('14:30')).toBe(870);
      expect(dateService.timeToMinutes('00:00')).toBe(0);
      expect(dateService.timeToMinutes('23:59')).toBe(1439);
    });

    it('should convert minutes to time string', () => {
      expect(dateService.minutesToTime(540)).toBe('09:00');
      expect(dateService.minutesToTime(870)).toBe('14:30');
      expect(dateService.minutesToTime(0)).toBe('00:00');
      expect(dateService.minutesToTime(1439)).toBe('23:59');
    });

    it('should get minutes since midnight', () => {
      const date = new Date(2024, 0, 15, 14, 30, 0);
      const minutes = dateService.getMinutesSinceMidnight(date);
      
      expect(minutes).toBe(870); // 14*60 + 30
    });

    it('should calculate duration in minutes', () => {
      const start = new Date(2024, 0, 15, 9, 0, 0);
      const end = new Date(2024, 0, 15, 10, 30, 0);
      const duration = dateService.getDurationMinutes(start, end);
      
      expect(duration).toBe(90);
    });

    it('should calculate duration from ISO strings', () => {
      const start = '2024-01-15T09:00:00.000Z';
      const end = '2024-01-15T10:30:00.000Z';
      const duration = dateService.getDurationMinutes(start, end);
      
      expect(duration).toBe(90);
    });
  });

  describe('Week Operations', () => {
    it('should get week bounds (Monday to Sunday)', () => {
      // Wednesday, January 17, 2024
      const date = new Date(2024, 0, 17);
      const bounds = dateService.getWeekBounds(date);
      
      // Should start on Monday, January 15
      expect(bounds.start.getDate()).toBe(15);
      expect(bounds.start.getDay()).toBe(1); // Monday
      
      // Should end on Sunday, January 21
      expect(bounds.end.getDate()).toBe(21);
      expect(bounds.end.getDay()).toBe(0); // Sunday
    });

    it('should add weeks', () => {
      const date = new Date(2024, 0, 15);
      const newDate = dateService.addWeeks(date, 2);
      
      expect(newDate.getDate()).toBe(29);
    });

    it('should subtract weeks', () => {
      const date = new Date(2024, 0, 15);
      const newDate = dateService.addWeeks(date, -1);
      
      expect(newDate.getDate()).toBe(8);
    });
  });

  describe('Grid Helpers', () => {
    it('should create date at specific time', () => {
      const baseDate = new Date(2024, 0, 15);
      const date = dateService.createDateAtTime(baseDate, 870); // 14:30
      
      expect(date.getHours()).toBe(14);
      expect(date.getMinutes()).toBe(30);
      expect(date.getDate()).toBe(15);
    });

    it('should snap to 15-minute interval', () => {
      const date = new Date(2024, 0, 15, 14, 37, 0); // 14:37
      const snapped = dateService.snapToInterval(date, 15);
      
      // 14:37 is closer to 14:30 than 14:45, so should snap to 14:30
      expect(snapped.getHours()).toBe(14);
      expect(snapped.getMinutes()).toBe(30);
    });

    it('should snap to 30-minute interval', () => {
      const date = new Date(2024, 0, 15, 14, 20, 0); // 14:20
      const snapped = dateService.snapToInterval(date, 30);
      
      // Should snap to 14:30
      expect(snapped.getHours()).toBe(14);
      expect(snapped.getMinutes()).toBe(30);
    });
  });

  describe('Utility Methods', () => {
    it('should check if same day', () => {
      const date1 = new Date(2024, 0, 15, 10, 0, 0);
      const date2 = new Date(2024, 0, 15, 14, 30, 0);
      const date3 = new Date(2024, 0, 16, 10, 0, 0);
      
      expect(dateService.isSameDay(date1, date2)).toBe(true);
      expect(dateService.isSameDay(date1, date3)).toBe(false);
    });

    it('should get start of day', () => {
      const date = new Date(2024, 0, 15, 14, 30, 45);
      const start = dateService.startOfDay(date);
      
      expect(start.getHours()).toBe(0);
      expect(start.getMinutes()).toBe(0);
      expect(start.getSeconds()).toBe(0);
    });

    it('should get end of day', () => {
      const date = new Date(2024, 0, 15, 14, 30, 45);
      const end = dateService.endOfDay(date);
      
      expect(end.getHours()).toBe(23);
      expect(end.getMinutes()).toBe(59);
      expect(end.getSeconds()).toBe(59);
    });

    it('should add days', () => {
      const date = new Date(2024, 0, 15);
      const newDate = dateService.addDays(date, 5);
      
      expect(newDate.getDate()).toBe(20);
    });

    it('should add minutes', () => {
      const date = new Date(2024, 0, 15, 10, 0, 0);
      const newDate = dateService.addMinutes(date, 90);
      
      expect(newDate.getHours()).toBe(11);
      expect(newDate.getMinutes()).toBe(30);
    });

    it('should parse ISO string', () => {
      const isoString = '2024-01-15T10:30:00.000Z';
      const date = dateService.parseISO(isoString);
      
      expect(date.toISOString()).toBe(isoString);
    });

    it('should validate dates', () => {
      const validDate = new Date(2024, 0, 15);
      const invalidDate = new Date('invalid');
      
      expect(dateService.isValid(validDate)).toBe(true);
      expect(dateService.isValid(invalidDate)).toBe(false);
    });
  });

  describe('Edge Cases', () => {
    it('should handle midnight', () => {
      const date = new Date(2024, 0, 15, 0, 0, 0);
      const minutes = dateService.getMinutesSinceMidnight(date);
      
      expect(minutes).toBe(0);
    });

    it('should handle end of day', () => {
      const date = new Date(2024, 0, 15, 23, 59, 0);
      const minutes = dateService.getMinutesSinceMidnight(date);
      
      expect(minutes).toBe(1439);
    });

    it('should handle cross-midnight duration', () => {
      const start = new Date(2024, 0, 15, 23, 0, 0);
      const end = new Date(2024, 0, 16, 1, 0, 0);
      const duration = dateService.getDurationMinutes(start, end);
      
      expect(duration).toBe(120); // 2 hours
    });
  });
});