package com.tibbo.aggregate.common.expression.function;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

import com.tibbo.aggregate.common.expression.Evaluator;
import com.tibbo.aggregate.common.expression.Expression;
import com.tibbo.aggregate.common.expression.function.date.DateCreateFunction;
import com.tibbo.aggregate.common.expression.function.date.DateDiffFunction;
import com.tibbo.aggregate.common.expression.function.date.ParseDateFunction;
import com.tibbo.aggregate.common.tests.CommonsFixture;
import com.tibbo.aggregate.common.tests.CommonsTestCase;

public class TestDateTimeFunctions extends CommonsTestCase
{
  
  private Evaluator ev;
  
  public void setUp() throws Exception
  {
    super.setUp();
    ev = CommonsFixture.createTestEvaluator();
  }

  public void testDateFunction() throws Exception
  {
    Date date = (Date) new DateCreateFunction().execute(ev, null, 2025, 1, 2, 3, 4, 5); // Zero-based month!
    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC"));

    assertEquals(2025, zonedDateTime.getYear());
    assertEquals(2, zonedDateTime.getMonthValue());
    assertEquals(2, zonedDateTime.getDayOfMonth());
    assertEquals(3, zonedDateTime.getHour());
    assertEquals(4, zonedDateTime.getMinute());
    assertEquals(5, zonedDateTime.getSecond());

    date = (Date) new DateCreateFunction().execute(ev, null, 1999, 11, 31); // Zero-based month!
    zonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC"));

    assertEquals(1999, zonedDateTime.getYear());
    assertEquals(12, zonedDateTime.getMonthValue());
    assertEquals(31, zonedDateTime.getDayOfMonth());

    date = (Date) new DateCreateFunction().execute(ev, null, null, null, null, 1, 2, 3);
    zonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC"));

    assertEquals(1, zonedDateTime.getHour());
    assertEquals(2, zonedDateTime.getMinute());
    assertEquals(3, zonedDateTime.getSecond());
  }
  
  public void testParseDateFunction() throws Exception
  {
    Date res = (Date) new ParseDateFunction().execute(ev, null, "2011-11-11 11:11:11", "yyyy-MM-dd HH:mm:ss");

    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(res.toInstant(), ZoneId.of("UTC"));

    assertEquals(2011, zonedDateTime.getYear());
    assertEquals(11, zonedDateTime.getMonthValue());
    assertEquals(11, zonedDateTime.getDayOfMonth());
    assertEquals(11, zonedDateTime.getHour());
    assertEquals(11, zonedDateTime.getMinute());
    assertEquals(11, zonedDateTime.getSecond());
  }
  
  public void testCalendarFunctionWithTimeZone() throws Exception
  {
    Date res = (Date) new ParseDateFunction().execute(ev, null, "2011-11-11 11:11:11", "yyyy-MM-dd HH:mm:ss", "UTC+03");

    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(res.toInstant(), ZoneId.of("UTC"));

    assertEquals(2011, zonedDateTime.getYear());
    assertEquals(11, zonedDateTime.getMonthValue());
    assertEquals(11, zonedDateTime.getDayOfMonth());
    assertEquals(8, zonedDateTime.getHour());
    assertEquals(11, zonedDateTime.getMinute());
    assertEquals(11, zonedDateTime.getSecond());
  }
  
  public void testDateDiffFunction() throws Exception
  {
    Date firstDate = (Date) ev.evaluate(new Expression("date(2019,7,31,12,0,0,0)"));
    assertNotNull(firstDate);
    Date secondDate = (Date) ev.evaluate(new Expression("date(2019,7,1,12,0,0,0)"));
    assertNotNull(secondDate);
    
    long difference = (long) new DateDiffFunction().execute(ev, null, firstDate, secondDate, "month");
    assertEquals(difference, 0);
    
    firstDate = (Date) ev.evaluate(new Expression("date(2019,6,1,11,0,0,0)"));
    assertNotNull(firstDate);
    difference = (long) new DateDiffFunction().execute(ev, null, firstDate, secondDate, "month");
    assertEquals(difference, 1);
    
    firstDate = (Date) ev.evaluate(new Expression("date(2020,0,1,12,0,0,0)"));
    secondDate = (Date) ev.evaluate(new Expression("date(2020,11,31,12,0,0,0)"));
    assertNotNull(firstDate);
    assertNotNull(secondDate);
    difference = (long) new DateDiffFunction().execute(ev, null, firstDate, secondDate, "month");
    assertEquals(difference, 11);
    
    difference = (long) new DateDiffFunction().execute(ev, null, firstDate, secondDate, "year");
    assertEquals(difference, 0);
  }
  
  public void testDateAddFunction() throws Exception
  {

    Date res = (Date) ev.evaluate(new Expression("dateAdd (parseDate (\"01.07.2021\", \"dd.MM.yyyy\"), -1, \"month\")"));

    LocalDate localDate = res.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

    assertEquals(2021, localDate.getYear());
    assertEquals(6, localDate.getMonthValue());
    assertEquals(1, localDate.getDayOfMonth());
  }
}
