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

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

import com.tibbo.aggregate.common.Cres;
import com.tibbo.aggregate.common.expression.EvaluationEnvironment;
import com.tibbo.aggregate.common.expression.EvaluationException;
import com.tibbo.aggregate.common.expression.Evaluator;
import com.tibbo.aggregate.common.expression.Function;
import com.tibbo.aggregate.common.expression.function.AbstractFunction;
import com.tibbo.aggregate.common.util.DateUtils;
import com.tibbo.aggregate.common.util.Util;
import org.apache.logging.log4j.util.Strings;

public class DateCreateFunction extends AbstractFunction
{
  public DateCreateFunction()
  {
    super("date", Function.GROUP_DATE_TIME_PROCESSING, "Integer year, Integer month, Integer day [, Integer hour, Integer minute, Integer second [, Integer millisecond [, String timezone]]]", "Date",
            Cres.get().getString("fDescDate"));
  }

  @Override
  public Object execute(Evaluator evaluator, EvaluationEnvironment environment, Object... parameters) throws EvaluationException
  {
    checkParameters(3, true, parameters);

    Number year = Util.convertToNumber(parameters[0], true, true);
    Number month = Util.convertToNumber(parameters[1], true, true);
    Number day = Util.convertToNumber(parameters[2], true, true);

    int hour = 0;
    int minute = 0;
    int second = 0;

    if (parameters.length > 5)
    {
      hour = Util.convertToNumber(parameters[3], true, false).intValue();
      minute = Util.convertToNumber(parameters[4], true, false).intValue();
      second = Util.convertToNumber(parameters[5], true, false).intValue();
    }

    int millisecond = 0;

    // Get current date-time as defaults
    ZonedDateTime now = ZonedDateTime.now();

    // Apply defaults for null values
    year = (year != null) ? year : now.getYear();
    month = (month != null) ? (month.intValue() + 1) : now.getMonthValue();
    day = (day != null) ? day : now.getDayOfMonth();

    ZoneId zoneId = DateUtils.UTC_TIME_ZONE.toZoneId();

    if (parameters.length > 6)
    {
      if (parameters[6] instanceof Long)
      {
        millisecond = Util.convertToNumber(parameters[6], true, false).intValue();
      }
      else if (parameters[6] instanceof String)
      {
        String zone = parameters[6].toString();
        if (!Strings.isEmpty(zone))
        {
          zoneId = ZoneId.of(zone);
        }
      }
    }
    if (parameters.length > 7)
    {
      String zone = parameters[7].toString();
      if (!Strings.isEmpty(zone))
      {
        zoneId = ZoneId.of(zone);
      }
    }

    // Using modern java.time API (recommended)
    ZonedDateTime zdt = ZonedDateTime.of(
            year.intValue(), month.intValue(), day.intValue(),
            hour, minute, second, millisToNanos(millisecond),
            zoneId
    );

    // Convert to java.util.Date
    return Date.from(zdt.toInstant());
  }

  public static int millisToNanos(long millis) {
    try {
      return (int) Math.multiplyExact(millis, 1_000_000L);
    } catch (ArithmeticException e) {
      throw new IllegalArgumentException("Millis value too large to convert to nanos");
    }
  }
}
