
package de.ivu.elect.business;

import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

/**
 * TODO nach technical verschieben
 */
public class DateUtil {

  private static final ZoneId ZONE_ID = ZoneId.systemDefault();

  private DateUtil() {
    super();
  }

  public static Date getNextMoToFr() {
    return getNextMoToFr(new Date());
  }

  public static Date getNextMoToFr(@Nullable Date value) {
    LocalDate localDate = DateUtil.asLocalDate(value);

    if (localDate == null) {
      localDate = LocalDate.now(ZONE_ID);
    }

    if (DayOfWeek.FRIDAY.equals(localDate.getDayOfWeek())) {
      localDate = localDate.plusDays(1);
    }

    if (DayOfWeek.SATURDAY.equals(localDate.getDayOfWeek())) {
      localDate = localDate.plusDays(1);
    }

    localDate = localDate.plusDays(1);

    return DateUtil.asDate(localDate);
  }

  public static Date getNextDayOfWeek(@Nullable Date value, DayOfWeek dayOfWeek) {
    LocalDate localDate = DateUtil.asLocalDate(value);

    if (localDate == null) {
      localDate = LocalDate.now(ZONE_ID);
    }

    localDate = localDate.plusDays(1); // Falls aktuell "Freitag", dann der nächste "Freitag"...

    while (!localDate.getDayOfWeek().equals(dayOfWeek)) {
      localDate = localDate.plusDays(1);
    }

    return DateUtil.asDate(localDate);
  }

  public static List<Date> getDatesBetween(Date startDate, Date endDate) {
    List<Date> result = new ArrayList<>();

    Calendar start = Calendar.getInstance();
    start.setTime(startDate);

    Calendar end = Calendar.getInstance();
    end.setTime(endDate);

    for (Date date = start.getTime(); start.before(end); start.add(Calendar.DATE, 1), date = start.getTime()) {
      result.add(date);
    }

    if (!endDate.before(startDate)) {
      result.add(endDate);
    }

    return result;
  }

  public static boolean isSameDay(Date one, Date two) {
    if (one == null || two == null) {
      return false;
    }

    if (one.equals(two)) {
      return true;
    }

    return DateUtils.isSameDay(one, two);
  }

  public static Date addDays(Date date, int amount) {
    return DateUtils.addDays(new Date(date.getTime()), amount);
  }

  public static Date beginnDesTages(Date date) {
    return DateUtils.truncate(new Date(date.getTime()), Calendar.DATE);
  }

  public static String getYear(@Nullable Date date) {
    if (date == null) {
      return StringUtils.EMPTY;
    }

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);

    return String.valueOf(calendar.get(Calendar.YEAR));
  }

  public static LocalDate asLocalDate(@Nullable Date value) {
    if (value == null) {
      return null;
    }

    return asZonedDateTime(value).map(ZonedDateTime::toLocalDate).orElse(null);
  }

  public static LocalDateTime asLocalDateTime(@Nullable Date value) {
    if (value == null) {
      return null;
    }

    return asZonedDateTime(value).map(ZonedDateTime::toLocalDateTime).orElse(null);
  }

  private static Optional<ZonedDateTime> asZonedDateTime(@Nullable Date value) {
    if (value == null) {
      return Optional.empty();
    }

    // NOTE, dass nicht Date#toInstant verwendet werden darf, da bspw. der Wahltermin aus der DB geladen wird und dieser (als java.sql.Date) keine Uhrzeit besitzt,
    // so dass eine UnsupportedOperationException beim Aufruf fliegen würde, cf. java.sql.Date#toInstant!!!
    return Optional.of(Instant.ofEpochMilli(value.getTime()).atZone(ZONE_ID));
  }

  public static Date asDate(@Nullable LocalDate value) {
    if (value == null) {
      return null;
    }

    return Date.from(value.atStartOfDay(ZONE_ID).toInstant());
  }

  public static Date asDate(@Nullable LocalDateTime value) {
    if (value == null) {
      return null;
    }

    return Date.from(value.atZone(ZONE_ID).toInstant());
  }

  public static boolean isMitternacht(java.util.Date datum) {
    return datum != null && datum.getHours() == 0 && datum.getMinutes() == 0 && datum.getSeconds() == 0;
  }
}
