package com.tibbo.aggregate.common.util;

import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;

import org.junit.Test;

public class TestDateUtils
{
  
  @Test
  public void testGetStartOfHour()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now(), LocalTime.of(LocalTime.now().getHour(), 0));
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getStartOfHour(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetEndOfHour()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now(), LocalTime.of(LocalTime.now().getHour(), 59, 59))
        .plus(999, ChronoField.MILLI_OF_DAY.getBaseUnit());
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getEndOfHour(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetStartOfDay()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getStartOfDay(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetEndOfDay()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59))
        .plus(999, ChronoField.MILLI_OF_DAY.getBaseUnit());
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getEndOfDay(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetStartOfWeek()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(DayOfWeek.MONDAY), LocalTime.of(0, 0));
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getStartOfWeek(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetEndOfWeek()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(DayOfWeek.SUNDAY), LocalTime.of(23, 59, 59))
        .plus(999, ChronoField.MILLI_OF_DAY.getBaseUnit());
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getEndOfWeek(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetStartOfMonth()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()), LocalTime.of(0, 0));
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getStartOfMonth(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetEndOfMonth()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()), LocalTime.of(23, 59, 59))
        .plus(999, ChronoField.MILLI_OF_DAY.getBaseUnit());
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getEndOfMonth(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetStartOfYear()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfYear()), LocalTime.of(0, 0));
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getStartOfYear(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testGetEndOfYear()
  {
    LocalDateTime expected = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfYear()), LocalTime.of(23, 59, 59))
        .plus(999, ChronoField.MILLI_OF_DAY.getBaseUnit());
    LocalDateTime actual = LocalDateTime.ofInstant(DateUtils.getEndOfYear(new Date()).toInstant(), ZoneId.systemDefault());
    assertEquals(expected, actual);
  }
  
  @Test
  public void testParseSmart() throws ParseException
  {
    LocalDateTime expectedDateTime = LocalDateTime.of(2025, Month.FEBRUARY, 2, 3, 0, 5);
    LocalDateTime actualDateTime = LocalDateTime.ofInstant(DateUtils.parseSmart("2025-02-02 03:00:05").toInstant(), ZoneId.systemDefault());
    assertEquals(expectedDateTime, actualDateTime);
    
    LocalTime expectedTime = LocalTime.of(7, 40, 25);
    LocalTime actualTime = DateUtils.parseSmart("7:40:25").toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
    assertEquals(expectedTime, actualTime);
    
    expectedTime = LocalTime.of(17, 40, 25);
    actualTime = DateUtils.parseSmart("5:40:25 PM").toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
    assertEquals(expectedTime, actualTime);
    
    expectedTime = LocalTime.of(17, 40, 25);
    actualTime = DateUtils.parseSmart("17:40:25").toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
    assertEquals(expectedTime, actualTime);
  }
}
