import { 
  convertToDateObject,
  getLocalDateFromString 
} from '../utils'

describe('Calendar Utils', () => {
  describe('convertToDateObject', () => {
    describe('Day Selection', () => {
      it('parses MM/DD/YYYY format correctly', () => {
        const result = convertToDateObject('2/16/2022', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(1) // February (0-based)
          expect(result.getDate()).toBe(16)
          expect(result.getHours()).toBe(0)
          expect(result.getMinutes()).toBe(0)
          expect(result.getSeconds()).toBe(0)
        }
      })

      it('parses MM/DD/YYYY with leading zeros', () => {
        const result = convertToDateObject('02/16/2022', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(1)
          expect(result.getDate()).toBe(16)
        }
      })

      it('parses single digit month and day', () => {
        const result = convertToDateObject('5/3/2022', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(4) // May (0-based)
          expect(result.getDate()).toBe(3)
        }
      })

      it('handles leap year correctly', () => {
        const result = convertToDateObject('2/29/2024', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2024)
          expect(result.getMonth()).toBe(1)
          expect(result.getDate()).toBe(29)
        }
      })

      it('handles invalid leap year date', () => {
        const result = convertToDateObject('2/29/2023', 'day')
        // Should still parse but JavaScript will normalize the date
        expect(result).toBeTruthy()
        if (result) {
          // Feb 29, 2023 becomes March 1, 2023
          expect(result.getFullYear()).toBe(2023)
          expect(result.getMonth()).toBe(2) // March
          expect(result.getDate()).toBe(1)
        }
      })

      it('handles year boundaries', () => {
        const result = convertToDateObject('12/31/2022', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(11) // December
          expect(result.getDate()).toBe(31)
        }
      })

      it('handles month with 31 days', () => {
        const result = convertToDateObject('1/31/2022', 'day')
        expect(result).toBeTruthy()
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(0) // January
          expect(result.getDate()).toBe(31)
        }
      })

      it('handles Date object input', () => {
        const inputDate = new Date(2022, 1, 16) // February 16, 2022
        const result = convertToDateObject(inputDate, 'day')
        expect(result).toBe(inputDate) // Should return the same object
      })

      it('handles invalid Date object', () => {
        const invalidDate = new Date('invalid')
        const result = convertToDateObject(invalidDate, 'day')
        expect(result).toBeNull()
      })

      it('handles null input', () => {
        const result = convertToDateObject(null as any, 'day')
        expect(result).toBeNull()
      })
    })

    describe('Timezone Handling', () => {
      it('maintains correct date across timezones', () => {
        const testCases = ['2/16/2022', '6/15/2023', '12/25/2023']
        
        testCases.forEach(dateString => {
          const result = convertToDateObject(dateString, 'day')
          expect(result).toBeTruthy()
          
          if (result) {
            const parts = dateString.split('/')
            const expectedMonth = parseInt(parts[0], 10)
            const expectedDay = parseInt(parts[1], 10)
            const expectedYear = parseInt(parts[2], 10)
            
            expect(result.getFullYear()).toBe(expectedYear)
            expect(result.getMonth() + 1).toBe(expectedMonth) // Convert from 0-based
            expect(result.getDate()).toBe(expectedDay)
            
            // Time should be midnight local time
            expect(result.getHours()).toBe(0)
            expect(result.getMinutes()).toBe(0)
            expect(result.getSeconds()).toBe(0)
          }
        })
      })

      it('does not shift dates due to timezone offset', () => {
        // This was the bug we fixed - dates were being shifted by timezone offset
        const result = convertToDateObject('2/16/2022', 'day')
        expect(result).toBeTruthy()
        
        if (result) {
          // The date should be exactly February 16, 2022 at midnight local time
          // NOT February 15, 2022 at 23:00 (which was the bug)
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(1) // February
          expect(result.getDate()).toBe(16)
          expect(result.getHours()).toBe(0)
        }
      })

      it('handles daylight saving time transitions', () => {
        // Test dates around DST transitions
        const springDST = convertToDateObject('3/12/2023', 'day') // Spring forward in US
        const fallDST = convertToDateObject('11/5/2023', 'day')   // Fall back in US
        
        expect(springDST).toBeTruthy()
        expect(fallDST).toBeTruthy()
        
        if (springDST) {
          expect(springDST.getFullYear()).toBe(2023)
          expect(springDST.getMonth()).toBe(2) // March
          expect(springDST.getDate()).toBe(12)
        }
        
        if (fallDST) {
          expect(fallDST.getFullYear()).toBe(2023)
          expect(fallDST.getMonth()).toBe(10) // November
          expect(fallDST.getDate()).toBe(5)
        }
      })
    })

    describe('Week Selection', () => {
      it('parses week strings correctly', () => {
        // Week format might be like "2022-W07" (ISO week format)
        const result = convertToDateObject('2022-W07', 'week')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          // Week 7 should be in February
          expect(result.getMonth()).toBeGreaterThanOrEqual(0)
          expect(result.getMonth()).toBeLessThanOrEqual(2)
        }
      })
    })

    describe('Month Selection', () => {
      it('parses month strings correctly', () => {
        const result = convertToDateObject('2022-02', 'month')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(1) // February
          expect(result.getDate()).toBe(1) // Should default to first day of month
        }
      })

      it('handles month boundaries', () => {
        const result = convertToDateObject('2022-12', 'month')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(11) // December
        }
      })
    })

    describe('Year Selection', () => {
      it('parses year strings correctly', () => {
        const result = convertToDateObject('2022', 'year')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(0) // Should default to January
          expect(result.getDate()).toBe(1) // Should default to first day
        }
      })

      it('handles different year formats', () => {
        const result = convertToDateObject('22', 'year')
        // This might not work depending on implementation
        // but we should handle it gracefully
        expect(result !== null).toBeDefined()
      })
    })

    describe('Locale Support', () => {
      it('parses dates with en-US locale', () => {
        const result = convertToDateObject('2/16/2022', 'day', 'en-US')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getMonth()).toBe(1) // February
          expect(result.getDate()).toBe(16)
        }
      })

      it('parses dates with different locale formats', () => {
        // European format (DD/MM/YYYY) might be handled differently
        const result = convertToDateObject('16/2/2022', 'day', 'en-GB')
        expect(result).toBeTruthy()
        
        if (result) {
          // Depending on implementation, this might parse as Feb 16 or error
          expect(result.getFullYear()).toBe(2022)
        }
      })

      it('handles invalid locale gracefully', () => {
        const result = convertToDateObject('2/16/2022', 'day', 'invalid-locale')
        // Behavior depends on the runtime — may fallback to default locale or return null
        if (result) {
          expect(result.getFullYear()).toBe(2022)
          expect(result.getMonth()).toBe(1)
          expect(result.getDate()).toBe(16)
        } else {
          expect(result).toBeNull()
        }
      })
    })

    describe('Edge Cases and Error Handling', () => {
      it('handles empty string', () => {
        const result = convertToDateObject('', 'day')
        expect(result).toBeNull()
      })

      it('handles whitespace-only string', () => {
        const result = convertToDateObject('   ', 'day')
        expect(result).toBeNull()
      })

      it('handles completely invalid date string', () => {
        const result = convertToDateObject('not-a-date', 'day')
        expect(result).toBeNull()
      })

      it('handles partial date strings', () => {
        const result = convertToDateObject('2/16', 'day')
        expect(result).toBeNull() // Should require complete date
      })

      it('handles invalid month', () => {
        const result = convertToDateObject('13/16/2022', 'day')
        // JavaScript Date will normalize this, so it might become valid
        // but our parser should handle it appropriately
        expect(result !== null).toBeDefined()
      })

      it('handles invalid day', () => {
        const result = convertToDateObject('2/32/2022', 'day')
        // Invalid day returns null
        expect(result).toBeNull()
      })

      it('handles very old dates', () => {
        const result = convertToDateObject('1/1/1900', 'day')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(1900)
          expect(result.getMonth()).toBe(0)
          expect(result.getDate()).toBe(1)
        }
      })

      it('handles far future dates', () => {
        const result = convertToDateObject('12/31/2099', 'day')
        expect(result).toBeTruthy()
        
        if (result) {
          expect(result.getFullYear()).toBe(2099)
          expect(result.getMonth()).toBe(11)
          expect(result.getDate()).toBe(31)
        }
      })

      it('handles negative years gracefully', () => {
        const result = convertToDateObject('1/1/-100', 'day')
        // This should probably return null or handle the error gracefully
        expect(result !== null).toBeDefined()
      })

      it('handles extremely large years', () => {
        const result = convertToDateObject('1/1/999999', 'day')
        // This should probably return null or handle the error gracefully
        expect(result !== null).toBeDefined()
      })
    })

    describe('Performance', () => {
      it('handles multiple rapid conversions', () => {
        const startTime = performance.now()
        const testDates = [
          '1/1/2022', '2/14/2022', '3/17/2022', '4/22/2022',
          '5/30/2022', '6/15/2022', '7/4/2022', '8/10/2022',
          '9/5/2022', '10/31/2022', '11/24/2022', '12/25/2022'
        ]
        
        testDates.forEach(dateString => {
          const result = convertToDateObject(dateString, 'day')
          expect(result).toBeTruthy()
        })
        
        const endTime = performance.now()
        const duration = endTime - startTime
        
        // Should complete in reasonable time (less than 100ms for 12 conversions)
        expect(duration).toBeLessThan(100)
      })
    })
  })

  describe('getLocalDateFromString', () => {
    it('provides enhanced locale-aware parsing', () => {
      const result = getLocalDateFromString('2/16/2022', 'en-US')
      expect(result).toBeTruthy()
      
      if (result) {
        expect(result.getFullYear()).toBe(2022)
        expect(result.getMonth()).toBe(1)
        expect(result.getDate()).toBe(16)
      }
    })

    it('supports time inclusion', () => {
      const result = getLocalDateFromString('2/16/2022 14:30', 'en-US', true)
      // Time-included strings may not be parseable depending on implementation
      // The function returns null for unsupported formats
      expect(result).toBeNull()
    })

    it('handles different selection types', () => {
      const dayResult = getLocalDateFromString('2/16/2022', 'en-US', false, 'day')
      const weekResult = getLocalDateFromString('2022-W07', 'en-US', false, 'week')
      const monthResult = getLocalDateFromString('2022-02', 'en-US', false, 'month')
      const yearResult = getLocalDateFromString('2022', 'en-US', false, 'year')
      
      expect(dayResult).toBeTruthy()
      expect(weekResult).toBeTruthy()
      expect(monthResult).toBeTruthy()
      expect(yearResult).toBeTruthy()
    })

    it('validates input parameters', () => {
      const result = getLocalDateFromString('', 'en-US')
      expect(result).toBeNull()
      
      const result2 = getLocalDateFromString(null as any, 'en-US')
      expect(result2).toBeNull()
      
      const result3 = getLocalDateFromString(undefined as any, 'en-US')
      expect(result3).toBeNull()
    })
  })
})