import { describe, expect, it } from 'vitest';
import { calculateDateConfidence } from '../helpers/date-confidence.helper.js';

describe('calculateDateConfidence', () => {
  describe('client-aware flag', () => {
    it('returns 1.0 for client same-business on same day', () => {
      const date = new Date('2024-01-15');
      const result = calculateDateConfidence(date, date, true);
      expect(result).toBe(1.0);
    });

    it('returns 1.0 for client same-business 30 days apart', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-31');
      const result = calculateDateConfidence(date1, date2, true);
      expect(result).toBe(1.0);
    });

    it('returns 1.0 for client same-business 365 days apart', () => {
      const date1 = new Date('2023-01-01');
      const date2 = new Date('2024-01-01');
      const result = calculateDateConfidence(date1, date2, true);
      expect(result).toBe(1.0);
    });

    it('uses standard degradation when not a client match', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-31');
      const result = calculateDateConfidence(date1, date2, false);
      expect(result).toBe(0.0);
    });
  });

  describe('same day', () => {
    it('should return 1.0 for identical dates', () => {
      const date = new Date('2024-01-15T10:30:00Z');
      const result = calculateDateConfidence(date, date);
      expect(result).toBe(1.0);
    });

    it('should return 1.0 for same day with different times', () => {
      const date1 = new Date('2024-01-15T08:00:00Z');
      const date2 = new Date('2024-01-15T18:30:00Z');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(1.0);
    });

    it('should return 1.0 for same day at different hours', () => {
      const date1 = new Date('2024-01-15T00:00:00');
      const date2 = new Date('2024-01-15T23:59:59');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(1.0);
    });
  });

  describe('specific day differences', () => {
    it('should return ~0.97 for 1 day difference', () => {
      const date1 = new Date('2024-01-15');
      const date2 = new Date('2024-01-16');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBeCloseTo(0.97, 2);
      expect(result).toBe(0.97);
    });

    it('should return ~0.77 for 7 days difference', () => {
      const date1 = new Date('2024-01-15');
      const date2 = new Date('2024-01-22');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBeCloseTo(0.77, 2);
      expect(result).toBe(0.77);
    });

    it('should return 0.5 for 15 days difference', () => {
      const date1 = new Date('2024-01-15');
      const date2 = new Date('2024-01-30');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });

    it('should return ~0.03 for 29 days difference', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-30');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBeCloseTo(0.03, 2);
      expect(result).toBe(0.03);
    });

    it('should return 0.0 for exactly 30 days difference', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-31');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.0);
    });

    it('should return 0.0 for 100 days difference', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-04-10');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.0);
    });

    it('should return 0.0 for 365 days difference', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2025-01-01');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.0);
    });
  });

  describe('order independence', () => {
    it('should return same result regardless of parameter order', () => {
      const date1 = new Date('2024-01-15');
      const date2 = new Date('2024-01-25');

      const result1 = calculateDateConfidence(date1, date2);
      const result2 = calculateDateConfidence(date2, date1);

      expect(result1).toBe(result2);
    });

    it('should work with date2 before date1', () => {
      const date1 = new Date('2024-01-30');
      const date2 = new Date('2024-01-15');

      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });

    it('should handle various orderings with same result', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-08');

      expect(calculateDateConfidence(date1, date2)).toBe(
        calculateDateConfidence(date2, date1),
      );
    });
  });

  describe('linear degradation formula', () => {
    it('should follow linear formula: 1.0 - (days_diff / 30)', () => {
      const testCases = [
        { days: 0, expected: 1.0 },
        { days: 3, expected: 0.9 },
        { days: 6, expected: 0.8 },
        { days: 9, expected: 0.7 },
        { days: 12, expected: 0.6 },
        { days: 15, expected: 0.5 },
        { days: 18, expected: 0.4 },
        { days: 21, expected: 0.3 },
        { days: 24, expected: 0.2 },
        { days: 27, expected: 0.1 },
        { days: 30, expected: 0.0 },
      ];

      testCases.forEach(({ days, expected }) => {
        const date1 = new Date('2024-01-01');
        const date2 = new Date(date1);
        date2.setDate(date1.getDate() + days);

        const result = calculateDateConfidence(date1, date2);
        expect(result).toBe(expected);
      });
    });

    it('should verify degradation is proportional', () => {
      const date1 = new Date('2024-01-01');

      const result5Days = calculateDateConfidence(date1, new Date('2024-01-06'));
      const result10Days = calculateDateConfidence(date1, new Date('2024-01-11'));
      const result20Days = calculateDateConfidence(date1, new Date('2024-01-21'));

      // Verify the relationship: each 5 days reduces confidence by ~0.17
      expect(result5Days - result10Days).toBeCloseTo(0.16, 1);
      expect(result10Days - result20Days).toBeCloseTo(0.33, 1);
    });
  });

  describe('different time zones', () => {
    it('should handle dates with different time zones correctly', () => {
      // Same calendar day in different time zones
      const date1 = new Date('2024-01-15T23:00:00-05:00'); // Eastern time
      const date2 = new Date('2024-01-15T20:00:00-08:00'); // Pacific time
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(1.0);
    });

    it('should calculate difference based on UTC calendar dates', () => {
      const date1 = new Date('2024-01-15T02:00:00+02:00');
      const date2 = new Date('2024-01-16T02:00:00+02:00');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.97);
    });
  });

  describe('leap years', () => {
    it('should handle leap year dates correctly', () => {
      const date1 = new Date('2024-02-28');
      const date2 = new Date('2024-02-29'); // Leap day
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.97);
    });

    it('should calculate across leap day correctly', () => {
      const date1 = new Date('2024-02-15');
      const date2 = new Date('2024-03-16'); // 30 days later (leap year)
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.0);
    });

    it('should handle non-leap year February', () => {
      const date1 = new Date('2023-02-01');
      const date2 = new Date('2023-03-03'); // 30 days later (non-leap year)
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.0);
    });
  });

  describe('month boundaries', () => {
    it('should handle dates across month boundaries', () => {
      const date1 = new Date('2024-01-31');
      const date2 = new Date('2024-02-15');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });

    it('should handle dates across year boundaries', () => {
      const date1 = new Date('2023-12-25');
      const date2 = new Date('2024-01-09');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });
  });

  describe('edge cases', () => {
    it('should handle very old dates', () => {
      const date1 = new Date('1990-01-01');
      const date2 = new Date('1990-01-16');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });

    it('should handle future dates', () => {
      const date1 = new Date('2030-06-15');
      const date2 = new Date('2030-06-30');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.5);
    });

    it('should handle dates at different times of day', () => {
      const date1 = new Date('2024-01-15T10:00:00');
      const date2 = new Date('2024-01-22T14:00:00');
      const result = calculateDateConfidence(date1, date2);
      expect(result).toBe(0.77);
    });
  });

  describe('precision and rounding', () => {
    it('should round to exactly 2 decimal places', () => {
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-02');
      const result = calculateDateConfidence(date1, date2);

      // Check that result has at most 2 decimal places
      expect(result.toString().split('.')[1]?.length || 0).toBeLessThanOrEqual(2);
    });

    it('should return values between 0.0 and 1.0', () => {
      const testDates = [
        [new Date('2024-01-01'), new Date('2024-01-01')], // 0 days
        [new Date('2024-01-01'), new Date('2024-01-11')], // 10 days
        [new Date('2024-01-01'), new Date('2024-01-21')], // 20 days
        [new Date('2024-01-01'), new Date('2024-02-01')], // 31 days
      ];

      testDates.forEach(([d1, d2]) => {
        const result = calculateDateConfidence(d1, d2);
        expect(result).toBeGreaterThanOrEqual(0.0);
        expect(result).toBeLessThanOrEqual(1.0);
      });
    });

    it('should handle fractional day calculations correctly', () => {
      // 2 days difference
      const date1 = new Date('2024-01-01');
      const date2 = new Date('2024-01-03');
      const result = calculateDateConfidence(date1, date2);

      // 1 - (2/30) = 1 - 0.0667 = 0.9333, rounded to 0.93
      expect(result).toBe(0.93);
    });
  });

  describe('return value validation', () => {
    it('should only return 0.0, 1.0, or values rounded to 2 decimals', () => {
      const results = [
        calculateDateConfidence(new Date('2024-01-01'), new Date('2024-01-01')), // 1.0
        calculateDateConfidence(new Date('2024-01-01'), new Date('2024-01-02')), // 0.97
        calculateDateConfidence(new Date('2024-01-01'), new Date('2024-01-16')), // 0.5
        calculateDateConfidence(new Date('2024-01-01'), new Date('2024-02-01')), // 0.0
      ];

      results.forEach(result => {
        const decimalPlaces = result.toString().split('.')[1]?.length || 0;
        expect(decimalPlaces).toBeLessThanOrEqual(2);
      });
    });

    it('should maintain consistency across multiple calls', () => {
      const date1 = new Date('2024-01-15');
      const date2 = new Date('2024-01-25');

      const result1 = calculateDateConfidence(date1, date2);
      const result2 = calculateDateConfidence(date1, date2);
      const result3 = calculateDateConfidence(date1, date2);

      expect(result1).toBe(result2);
      expect(result2).toBe(result3);
    });
  });
});
