import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  toUTC,
  toUTCFromTimezone,
  convertTimezoneToUTC,
  fromUTC,
  convertUTCToTimezone,
  getDisplayTimezone,
  getUserTimezone,
  formatForDisplay,
  formatTimeForDisplay,
  formatDateForDisplay,
  formatForDateTimeInput,
  formatLocalDate,
  parseLocalDate,
  formatLocalDateTime,
  getTodayInTimezone,
  isToday,
  isSameDay,
  startOfDay,
  endOfDay,
  getTimezoneAbbreviation,
  formatTimezoneDisplay,
} from '../dateUtils';

describe('dateUtils', () => {
  describe('toUTC', () => {
    it('converts Date to ISO string', () => {
      const date = new Date('2024-12-08T14:00:00Z');
      expect(toUTC(date)).toBe('2024-12-08T14:00:00.000Z');
    });

    it('preserves milliseconds', () => {
      const date = new Date('2024-12-08T14:00:00.123Z');
      expect(toUTC(date)).toBe('2024-12-08T14:00:00.123Z');
    });
  });

  describe('formatLocalDate', () => {
    it('formats date as YYYY-MM-DD', () => {
      const date = new Date(2024, 11, 8); // Dec 8, 2024
      expect(formatLocalDate(date)).toBe('2024-12-08');
    });

    it('pads single-digit months', () => {
      const date = new Date(2024, 0, 15); // Jan 15, 2024
      expect(formatLocalDate(date)).toBe('2024-01-15');
    });

    it('pads single-digit days', () => {
      const date = new Date(2024, 11, 5); // Dec 5, 2024
      expect(formatLocalDate(date)).toBe('2024-12-05');
    });
  });

  describe('parseLocalDate', () => {
    it('parses YYYY-MM-DD to local Date', () => {
      const date = parseLocalDate('2024-12-08');
      expect(date.getFullYear()).toBe(2024);
      expect(date.getMonth()).toBe(11); // December (0-indexed)
      expect(date.getDate()).toBe(8);
    });

    it('creates date at midnight', () => {
      const date = parseLocalDate('2024-06-15');
      expect(date.getHours()).toBe(0);
      expect(date.getMinutes()).toBe(0);
    });
  });

  describe('formatLocalDateTime', () => {
    it('formats Date as YYYY-MM-DDTHH:MM', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      expect(formatLocalDateTime(date)).toBe('2024-12-08T14:30');
    });

    it('pads hours and minutes', () => {
      const date = new Date(2024, 0, 5, 9, 5);
      expect(formatLocalDateTime(date)).toBe('2024-01-05T09:05');
    });
  });

  describe('isSameDay', () => {
    it('returns true for same day', () => {
      const date1 = new Date(2024, 11, 8, 10, 0);
      const date2 = new Date(2024, 11, 8, 22, 30);
      expect(isSameDay(date1, date2)).toBe(true);
    });

    it('returns false for different days', () => {
      const date1 = new Date(2024, 11, 8);
      const date2 = new Date(2024, 11, 9);
      expect(isSameDay(date1, date2)).toBe(false);
    });

    it('returns false for different months', () => {
      const date1 = new Date(2024, 10, 8);
      const date2 = new Date(2024, 11, 8);
      expect(isSameDay(date1, date2)).toBe(false);
    });

    it('returns false for different years', () => {
      const date1 = new Date(2023, 11, 8);
      const date2 = new Date(2024, 11, 8);
      expect(isSameDay(date1, date2)).toBe(false);
    });
  });

  describe('startOfDay', () => {
    it('sets time to midnight', () => {
      const date = new Date(2024, 11, 8, 14, 30, 45, 123);
      const result = startOfDay(date);
      expect(result.getHours()).toBe(0);
      expect(result.getMinutes()).toBe(0);
      expect(result.getSeconds()).toBe(0);
      expect(result.getMilliseconds()).toBe(0);
    });

    it('preserves the date', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      const result = startOfDay(date);
      expect(result.getFullYear()).toBe(2024);
      expect(result.getMonth()).toBe(11);
      expect(result.getDate()).toBe(8);
    });

    it('does not mutate original date', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      startOfDay(date);
      expect(date.getHours()).toBe(14);
    });
  });

  describe('endOfDay', () => {
    it('sets time to 23:59:59.999', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      const result = endOfDay(date);
      expect(result.getHours()).toBe(23);
      expect(result.getMinutes()).toBe(59);
      expect(result.getSeconds()).toBe(59);
      expect(result.getMilliseconds()).toBe(999);
    });

    it('preserves the date', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      const result = endOfDay(date);
      expect(result.getFullYear()).toBe(2024);
      expect(result.getMonth()).toBe(11);
      expect(result.getDate()).toBe(8);
    });

    it('does not mutate original date', () => {
      const date = new Date(2024, 11, 8, 14, 30);
      endOfDay(date);
      expect(date.getHours()).toBe(14);
    });
  });

  describe('getDisplayTimezone', () => {
    it('returns business timezone when provided', () => {
      expect(getDisplayTimezone('America/Denver')).toBe('America/Denver');
    });

    it('returns business timezone for empty string', () => {
      // Empty string is falsy, so should use browser timezone
      const result = getDisplayTimezone('');
      expect(result).toBeTruthy();
      // Should be the browser's timezone
    });

    it('returns browser timezone when null', () => {
      const result = getDisplayTimezone(null);
      expect(result).toBeTruthy();
    });

    it('returns browser timezone when undefined', () => {
      const result = getDisplayTimezone(undefined);
      expect(result).toBeTruthy();
    });
  });

  describe('getUserTimezone', () => {
    it('returns a valid IANA timezone', () => {
      const tz = getUserTimezone();
      expect(tz).toBeTruthy();
      expect(typeof tz).toBe('string');
      // Should contain a slash (e.g., America/New_York)
      expect(tz).toMatch(/\//);
    });
  });

  describe('formatForDisplay', () => {
    it('formats UTC string with timezone', () => {
      const result = formatForDisplay('2024-12-08T19:00:00Z', 'America/Denver');
      // Should be Dec 8, 2024, 12:00 PM in Mountain Time
      expect(result).toContain('Dec');
      expect(result).toContain('8');
      expect(result).toContain('2024');
    });

    it('accepts custom options', () => {
      const result = formatForDisplay('2024-12-08T19:00:00Z', 'UTC', {
        year: undefined,
        month: 'long',
      });
      expect(result).toContain('December');
    });
  });

  describe('formatTimeForDisplay', () => {
    it('formats only time portion', () => {
      const result = formatTimeForDisplay('2024-12-08T19:00:00Z', 'UTC');
      expect(result).toMatch(/\d{1,2}:\d{2}\s*(AM|PM)/);
    });

    it('respects timezone', () => {
      // 19:00 UTC should be 12:00 PM in Denver (MST, -7)
      const result = formatTimeForDisplay('2024-12-08T19:00:00Z', 'America/Denver');
      expect(result).toContain('12:00');
      expect(result).toContain('PM');
    });
  });

  describe('formatDateForDisplay', () => {
    it('formats only date portion', () => {
      const result = formatDateForDisplay('2024-12-08T19:00:00Z', 'UTC');
      expect(result).toContain('Dec');
      expect(result).toContain('8');
      expect(result).toContain('2024');
    });

    it('does not include time', () => {
      const result = formatDateForDisplay('2024-12-08T19:00:00Z', 'UTC');
      expect(result).not.toMatch(/\d{1,2}:\d{2}/);
    });

    it('accepts custom options', () => {
      const result = formatDateForDisplay('2024-12-08T19:00:00Z', 'UTC', {
        weekday: 'long',
      });
      expect(result).toContain('Sunday');
    });
  });

  describe('getTodayInTimezone', () => {
    it('returns YYYY-MM-DD format', () => {
      const result = getTodayInTimezone('UTC');
      expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    });

    it('respects timezone', () => {
      // At certain times, today in UTC vs Tokyo might differ
      const utc = getTodayInTimezone('UTC');
      const tokyo = getTodayInTimezone('Asia/Tokyo');
      // Both should be valid dates
      expect(utc).toMatch(/^\d{4}-\d{2}-\d{2}$/);
      expect(tokyo).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    });
  });

  describe('isToday', () => {
    it('returns true for today', () => {
      const today = getTodayInTimezone('UTC');
      expect(isToday(today, 'UTC')).toBe(true);
    });

    it('returns false for yesterday', () => {
      const yesterday = new Date();
      yesterday.setDate(yesterday.getDate() - 1);
      const yesterdayStr = formatLocalDate(yesterday);
      expect(isToday(yesterdayStr, 'UTC')).toBe(false);
    });
  });

  describe('getTimezoneAbbreviation', () => {
    it('returns abbreviation for known timezone', () => {
      // Note: Abbreviation may vary by date (DST)
      const abbr = getTimezoneAbbreviation('America/New_York');
      expect(['EST', 'EDT']).toContain(abbr);
    });

    it('returns abbreviation for UTC', () => {
      const abbr = getTimezoneAbbreviation('UTC');
      expect(abbr).toBe('UTC');
    });

    it('uses provided date for DST calculation', () => {
      // Winter date (should be MST)
      const winter = new Date('2024-01-15');
      const winterAbbr = getTimezoneAbbreviation('America/Denver', winter);
      expect(winterAbbr).toBe('MST');

      // Summer date (should be MDT)
      const summer = new Date('2024-07-15');
      const summerAbbr = getTimezoneAbbreviation('America/Denver', summer);
      expect(summerAbbr).toBe('MDT');
    });
  });

  describe('formatTimezoneDisplay', () => {
    it('formats timezone with city name and abbreviation', () => {
      const result = formatTimezoneDisplay('America/Denver');
      expect(result).toContain('Denver');
      expect(result).toMatch(/\(M[SD]T\)/);
    });

    it('handles underscores in city names', () => {
      const result = formatTimezoneDisplay('America/New_York');
      expect(result).toContain('New York');
    });

    it('handles UTC', () => {
      const result = formatTimezoneDisplay('UTC');
      expect(result).toContain('UTC');
    });
  });

  describe('convertUTCToTimezone', () => {
    it('converts UTC date to target timezone', () => {
      const utcDate = new Date('2024-12-08T19:00:00Z');
      const result = convertUTCToTimezone(utcDate, 'America/Denver');
      // 19:00 UTC = 12:00 MST
      expect(result.getHours()).toBe(12);
      expect(result.getMinutes()).toBe(0);
    });

    it('handles date crossing', () => {
      // Late UTC time might be previous day in western timezones
      const utcDate = new Date('2024-12-08T02:00:00Z');
      const result = convertUTCToTimezone(utcDate, 'America/Los_Angeles');
      // 02:00 UTC = 18:00 PST (previous day)
      expect(result.getDate()).toBe(7);
    });
  });

  describe('fromUTC', () => {
    it('converts UTC string to date in business timezone', () => {
      const result = fromUTC('2024-12-08T19:00:00Z', 'America/Denver');
      expect(result.getHours()).toBe(12); // MST
    });

    it('uses local timezone when business timezone is null', () => {
      const result = fromUTC('2024-12-08T19:00:00Z', null);
      // Should return a valid date
      expect(result instanceof Date).toBe(true);
    });
  });

  describe('formatForDateTimeInput', () => {
    it('returns datetime-local format', () => {
      const result = formatForDateTimeInput('2024-12-08T19:00:00Z', 'UTC');
      expect(result).toBe('2024-12-08T19:00');
    });

    it('respects timezone conversion', () => {
      const result = formatForDateTimeInput('2024-12-08T19:00:00Z', 'America/Denver');
      // 19:00 UTC = 12:00 MST
      expect(result).toBe('2024-12-08T12:00');
    });
  });

  describe('toUTCFromTimezone', () => {
    it('converts date and time in timezone to UTC', () => {
      const date = new Date(2024, 11, 8); // Dec 8, 2024
      const result = toUTCFromTimezone(date, '12:00', 'America/Denver');
      // 12:00 MST = 19:00 UTC
      const resultDate = new Date(result);
      expect(resultDate.getUTCHours()).toBe(19);
    });
  });

  describe('convertTimezoneToUTC', () => {
    it('converts timezone date to UTC', () => {
      // Create a date representing 12:00 in Denver
      const localDate = new Date(2024, 11, 8, 12, 0, 0);
      const result = convertTimezoneToUTC(localDate, 'America/Denver');
      // 12:00 MST = 19:00 UTC
      expect(result.getUTCHours()).toBe(19);
    });
  });
});
