package ru.yandex.webmaster3.core.util;

import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import com.datastax.driver.core.utils.UUIDs;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.joda.time.base.AbstractInstant;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;


/**
 * @author aherman
 */
public class TimeUtils {
    public static final TimeZone ZONE_MSK = TimeZone.getTimeZone("Europe/Moscow");
    public static final DateTimeZone EUROPE_MOSCOW_ZONE = DateTimeZone.forID("Europe/Moscow");

    public static final DateTimeFormatter DF_YYYYMMDD_HHMMSS = DateTimeFormat.forPattern("yyyyMMdd_HHmmss");
    public static final DateTimeFormatter DF_YYYYMMDDHHMMSS_WRITE = DateTimeFormat.forPattern("yyyyMMddHHmmss");
    public static final DateTimeFormatter DF_YYYYMMDDHHMMSS_READ = localDateOptionalTimeParser();

    public static final DateTimeFormatter DF_YYYYMMDD_HHMMSS_MSK = DateTimeFormat.forPattern("yyyyMMdd_HHmmss").withZone(EUROPE_MOSCOW_ZONE);

    public static final DateTimeFormatter DF_YYYYMMDD_HHMM = DateTimeFormat.forPattern("yyyyMMdd_HHmm");
    public static final DateTimeFormatter DF_YYYYMMDD_HHMM_MSK = DateTimeFormat.forPattern("yyyyMMdd_HHmm").withZone(EUROPE_MOSCOW_ZONE);

    public static final DateTimeFormatter DF_YYYYMMDD = DateTimeFormat.forPattern("yyyyMMdd");
    public static final DateTimeFormatter DF_YYYYMMDD_MSK = DateTimeFormat.forPattern("yyyyMMdd").withZone(EUROPE_MOSCOW_ZONE);
    public static final long MILLIS_IN_HOUR = TimeUnit.HOURS.toMillis(1);

    public static Instant unixTimestampToInstant(int unixTimestamp) {
        return new Instant(TimeUnit.SECONDS.toMillis((long) unixTimestamp));
    }

    public static int instantToUnixTimestamp(Instant instant) {
        return toUnixTimestamp(instant);
    }

    public static int toUnixTimestamp(Instant instant) {
        return (int) TimeUnit.MILLISECONDS.toSeconds(instant.getMillis());
    }

    public static int toUnixTimestamp(DateTime dateTime) {
        return instantToUnixTimestamp(dateTime.toInstant());
    }

    public static DateTime unixTimestampToDate(int unixTimestamp, DateTimeZone timeZone) {
        return new DateTime(TimeUnit.SECONDS.toMillis(unixTimestamp), timeZone);
    }

    public static LocalDate instantToMSKDate(AbstractInstant instant) {
        return instant.toDateTime(EUROPE_MOSCOW_ZONE).toLocalDate();
    }

    /**
     * Сравнивает AbstractInstant'ы по timestamp'у - т.е., например, два DateTime'а в разных зонах будут сравниваться
     * исключительно по абсолютному времени
     * @param a
     * @param b
     * @return результат сравнения с учетом nullability
     */
    public static boolean instantEquals(AbstractInstant a, AbstractInstant b) {
        if (a == null) {
            return b != null;
        }
        if (b == null) {
            return false;
        }
        return a.getMillis() == b.getMillis();
    }

    public static int toUnixTimestamp(UUID uuid) {
        return (int) (UUIDs.unixTimestamp(uuid) / 1000);
    }

    public static <T extends Comparable<? super T>> T latestOf(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }

    public static <T extends Comparable<? super T>> T earliestOf(T a, T b) {
        return a.compareTo(b) < 0 ? a : b;
    }

    public static <T, C extends Comparable<? super C>> T latestBy(Function<T, C> toComparable, T a, T b) {
        C aTime = toComparable.apply(a);
        C bTime = toComparable.apply(b);
        return aTime.compareTo(bTime) > 0 ? a : b;
    }

    public static <T, C extends Comparable<? super C>> T earliestBy(Function<T, C> toComparable, T a, T b) {
        C aTime = toComparable.apply(a);
        C bTime = toComparable.apply(b);
        return aTime.compareTo(bTime) < 0 ? a : b;
    }

    private static DateTimeFormatter localDateOptionalTimeParser() {
        return new DateTimeFormatterBuilder()
                .append(DateTimeFormat.forPattern("yyyyMMdd").getParser())
                .appendOptional(DateTimeFormat.forPattern("HHmmss").getParser())
                .toFormatter();
    }

    public static long getCurrentHourInterval() {
        return System.currentTimeMillis() / MILLIS_IN_HOUR;
    }

    public static long toHourInterval(long millis) {
        return millis / MILLIS_IN_HOUR;
    }

}
