package it.integry.integrywmsnative.core.utility;

import androidx.annotation.Nullable;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import it.integry.integrywmsnative.core.exception.DateNotRecognizedException;
import it.integry.integrywmsnative.core.exception.TimeNotRecognizedException;

/**
 * Created by GiuseppeS on 07/03/2018.
 */

public class UtilityDate {

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

    public static class COMMONS_DATE_FORMATS {
        public static final String DMY_SLASH = "dd/MM/yyyy";
        public static final String YMD_SLASH = "yyyy/MM/dd";
        public static final String DM_SLASH = "dd/MM";
        public static final String MD_SLASH = "MM/dd";
        public static final String YMD_DASH = "yyyy-MM-dd";
        public static final String DMY_TIME_SLASH = DMY_SLASH + " HH:mm:ss";
        public static final String YMD_TIME_SLASH = YMD_SLASH + " HH:mm:ss";
        public static final String DM_TIME_SLASH = DM_SLASH + " HH:mm";
        public static final String MD_TIME_SLASH = MD_SLASH + " HH:mm";
        public static final String TIME = "HH:mm";
        public static final String TIME_W_SECS = "HH:mm:ss";


        public static final String DM_HUMAN = "dd MMM";
        public static final String DMY_HUMAN = "dd MMM yyyy";
        public static final String DMY_HUMAN_LONG = "dd MMMM yyyy";
    }

    public static Date recognizeDateWithExceptionHandler(String dateString) {
        try {
            return UtilityDate.recognizeDate(dateString);
        } catch (ParseException | DateNotRecognizedException | TimeNotRecognizedException pex) {
            UtilityLogger.error(pex);
            return null;
        }
    }

    public static Date recognizeDate(String dateString) throws ParseException, DateNotRecognizedException, TimeNotRecognizedException {
        if(dateString == null) return null;

        final String format = recognizeDateFormat(dateString);
        if(format == null) return null;

        SimpleDateFormat sdf = new SimpleDateFormat(format);

        return sdf.parse(dateString);
    }

    public static LocalDate recognizeLocalDateWithExceptionHandler(String dateString) {
        try {
            return UtilityDate.recognizeLocalDate(dateString);
        } catch (DateNotRecognizedException | TimeNotRecognizedException pex) {
            UtilityLogger.error(pex);
            return null;
        }
    }

    public static LocalDate recognizeLocalDate(String dateString) throws DateNotRecognizedException, TimeNotRecognizedException {
        final String format = recognizeDateFormat(dateString);
        if(format == null) return null;

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDate.parse(dateString, formatter);
    }

    public static LocalDateTime recognizeLocalDateTimeWithExceptionHandler(String dateString) {
        try {
            return UtilityDate.recognizeLocalDateTime(dateString);
        } catch (DateNotRecognizedException | TimeNotRecognizedException pex) {
            UtilityLogger.error(pex);
            return null;
        }
    }

    public static LocalDateTime recognizeLocalDateTime(String dateString) throws DateNotRecognizedException, TimeNotRecognizedException {
        final String format = recognizeDateFormat(dateString);
        if(format == null) return null;

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(dateString, formatter);
    }

    private static String recognizeDateFormat(String dateString) throws DateNotRecognizedException, TimeNotRecognizedException {
        if (dateString == null) {
            return null;
        }

        //SE SONO PIU' DI 10 caratteri allora c'è anche l'ora

        char dateSeparator;

        String onlyDateSubstring = dateString.substring(0, 10);
        if (onlyDateSubstring.contains("/")) dateSeparator = '/';
        else if (onlyDateSubstring.contains("-")) dateSeparator = '-';
        else if (onlyDateSubstring.contains(".")) dateSeparator = '.';
        else throw new DateNotRecognizedException(dateString);

        String dateFormatString = (dateString.charAt(2) == dateSeparator)
                ? "dd" + dateSeparator + "MM" + dateSeparator + "yyyy"
                : "yyyy" + dateSeparator + "MM" + dateSeparator + "dd";


        if (dateString.length() > 10) {

            char timeSeparator;

            String onlyTimeSubstring = dateString.substring(10, 14);
            if (onlyTimeSubstring.contains("-")) timeSeparator = '-';
            else if (onlyTimeSubstring.contains(":")) timeSeparator = ':';
            else throw new TimeNotRecognizedException(dateString);

            String timeFormatString = "HH" + timeSeparator + "mm" + timeSeparator + "ss";

            return dateFormatString + " " + timeFormatString;


        } else {
            return dateFormatString;
        }
    }


    public static String formatDate(Date dateToFormat, String format) {
        if (dateToFormat != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            //sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
            return sdf.format(dateToFormat);
        } else return null;
    }


    public static Date getDateInstance() {
        return getDateInstance(false);
    }


    public static Date getDateInstance(boolean removeTime) {
        var calendar = getCalendarInstance();

        if (removeTime) {
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
        }

        return calendar.getTime();
    }

    public static Calendar getCalendarInstance() {
        return Calendar.getInstance();
    }


    public static String formatDate(LocalDate dateToFormat, String format) {
        if (dateToFormat != null) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
            return dateToFormat.format(formatter);
        } else return null;
    }


    public static String formatDate(LocalDateTime dateToFormat, String format) {
        if (dateToFormat != null) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
            return dateToFormat.format(formatter);
        } else return null;
    }

    public static String formatDate(String dateToFormat, String format) {
        if (dateToFormat != null) {
            LocalDateTime data = recognizeLocalDateTimeWithExceptionHandler(dateToFormat);
            if (data != null) {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
                return data.format(dateTimeFormatter);
            }else return null;
        } else return null;
    }

    public static boolean isBeforeToday(LocalDateTime inputDate) {
        return inputDate != null && inputDate.toLocalDate().isBefore(getNow());
    }

    public static boolean isAfterToday(LocalDateTime inputDate) {
        return inputDate != null && inputDate.toLocalDate().isAfter(getNow());
    }

    public static boolean isToday(LocalDateTime inputDate) {
        return inputDate != null && inputDate.toLocalDate().isEqual(getNow());
    }

    public static boolean isBeforeToday(LocalDate inputDate) {
        return inputDate != null && inputDate.isBefore(getNow());
    }

    public static boolean isAfterToday(LocalDate inputDate) {
        return inputDate != null && inputDate.isAfter(getNow());
    }

    public static boolean isToday(LocalDate inputDate) {
        return inputDate != null && inputDate.isEqual(getNow());
    }

    public static LocalDate toLocalDate(Date dateToConvert) {
        return dateToConvert != null ? dateToConvert.toInstant()
                .atZone(currentZone)
                .toLocalDate() : null;
    }

    public static LocalDateTime toLocalDateTime(Date dateToConvert) {
        return dateToConvert != null ? dateToConvert.toInstant()
                .atZone(currentZone)
                .toLocalDateTime() : null;
    }

    public static Date toDate(LocalDate dateToConvert) {
        return dateToConvert != null ? Date.from(dateToConvert.atStartOfDay()
                .atZone(currentZone)
                .toInstant()) : null;
    }

    public static Date toDateTime(LocalDateTime dateToConvert) {
        return dateToConvert != null ? Date.from(dateToConvert
                .atZone(currentZone)
                .toInstant()) : null;
    }


    public static LocalDate getNow() {
        return Instant.now()
                .atZone(currentZone)
                .toLocalDate();
    }

    public static LocalDateTime getNowTime() {
        return Instant.now()
                .atZone(currentZone)
                .toLocalDateTime();
    }

    public static LocalDate millisTimeToLocalDate(long time, @Nullable String timezone) {
        return Instant.ofEpochMilli(time)
                .atZone(timezone == null ? currentZone : ZoneId.of(timezone))
                .toLocalDate();
    }

    public static LocalDate timeToLocalDate(long time, @Nullable String timezone) {
        return Instant.ofEpochSecond(time)
                .atZone(timezone == null ? currentZone : ZoneId.of(timezone))
                .toLocalDate();
    }

    public static LocalDateTime timeToLocalDateTime(long time, @Nullable String timezone) {
        return Instant.ofEpochSecond(time)
                .atZone(timezone == null ? currentZone : ZoneId.of(timezone))
                .toLocalDateTime();
    }


    public static long localDateTimeToTime(LocalDateTime localDateTime, @Nullable String timezone) {
        return localDateTime
                .atZone(timezone == null ? currentZone : ZoneId.of(timezone))
                .toEpochSecond();
    }

    public static long localDateToTime(LocalDate localDate) {
        return TimeUnit.DAYS.toSeconds(localDate.toEpochDay());
    }

    public static long localDateToMillisTime(LocalDate localDate) {
        return TimeUnit.DAYS.toMillis(localDate.toEpochDay());
    }


}
