package easydo.technology.util;


import easydo.technology.utils.StringUtil;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.temporal.TemporalAmount;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author liy
 * @since 2022-04-01
 **/
public class DateUtils {
    private static final ThreadLocal<Map<Integer, Boolean>> DATE_THREAD_LOCAL = new ThreadLocal();
    private static final ThreadLocal<Map<String, SimpleDateFormat>> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal();
    private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");
    private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");
    private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]");
    private static final Pattern date_ptrn3b = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/年月日,. :\"\\\\]+0*[ampAMP/]*$");
    private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");
    private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]");
    private static final Pattern date_ptrn6 = Pattern.compile("(年|月|日|时|分|秒)+");
    public static final String DATE_FORMAT_10 = "yyyy-MM-dd";
    public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss";
    public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss";
    public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_19_FORWARD_SLASH = "yyyy/MM/dd HH:mm:ss";
    private static final String MINUS = "-";

    private DateUtils() {
    }

    public static Date plusDays(Date date, long daysToAdd) {
        return plus(date, Duration.ofDays(daysToAdd));
    }

    public static Date plus(Date date, TemporalAmount amount) {
        Instant instant = date.toInstant();
        return Date.from(instant.plus(amount));
    }

    public static Date parseDate(String dateString, String dateFormat) throws ParseException {
        if (StringUtil.isEmpty(dateFormat)) {
            dateFormat = switchDateFormat(dateString);
        }

        return getCacheDateFormat(dateFormat).parse(dateString);
    }

    public static Date parseDate(String dateString) throws ParseException {
        return parseDate(dateString, switchDateFormat(dateString));
    }

    private static String switchDateFormat(String dateString) {
        int length = dateString.length();
        switch (length) {
            case 10:
                return "yyyy-MM-dd";
            case 11:
            case 12:
            case 13:
            case 15:
            case 16:
            case 18:
            default:
                throw new IllegalArgumentException("can not find date format for：" + dateString);
            case 14:
                return "yyyyMMddHHmmss";
            case 17:
                return "yyyyMMdd HH:mm:ss";
            case 19:
                return dateString.contains("-") ? "yyyy-MM-dd HH:mm:ss" : "yyyy/MM/dd HH:mm:ss";
        }
    }

    public static String format(Date date) {
        return format(date, (String) null);
    }

    public static String format(Date date, String dateFormat) {
        if (date == null) {
            return "";
        } else {
            if (StringUtil.isEmpty(dateFormat)) {
                dateFormat = "yyyy-MM-dd HH:mm:ss";
            }

            return getCacheDateFormat(dateFormat).format(date);
        }
    }

    private static DateFormat getCacheDateFormat(String dateFormat) {
        Map<String, SimpleDateFormat> dateFormatMap = (Map) DATE_FORMAT_THREAD_LOCAL.get();
        SimpleDateFormat dateFormatCached;
        if (dateFormatMap == null) {
            dateFormatMap = new HashMap();
            DATE_FORMAT_THREAD_LOCAL.set(dateFormatMap);
        } else {
            dateFormatCached = (SimpleDateFormat) ((Map) dateFormatMap).get(dateFormat);
            if (dateFormatCached != null) {
                return dateFormatCached;
            }
        }

        dateFormatCached = new SimpleDateFormat(dateFormat);
        ((Map) dateFormatMap).put(dateFormat, dateFormatCached);
        return dateFormatCached;
    }

    public static boolean isADateFormat(Integer formatIndex, String formatString) {
        if (formatIndex == null) {
            return false;
        } else {
            Map<Integer, Boolean> isDateCache = (Map) DATE_THREAD_LOCAL.get();
            if (isDateCache == null) {
                isDateCache = new HashMap();
                DATE_THREAD_LOCAL.set(isDateCache);
            } else {
                Boolean isDateCachedData = (Boolean) ((Map) isDateCache).get(formatIndex);
                if (isDateCachedData != null) {
                    return isDateCachedData;
                }
            }

            boolean isDate = isADateFormatUncached(formatIndex, formatString);
            ((Map) isDateCache).put(formatIndex, isDate);
            return isDate;
        }
    }

    public static boolean isADateFormatUncached(Integer formatIndex, String formatString) {
        if (isInternalDateFormat(formatIndex)) {
            return true;
        } else if (StringUtil.isEmpty(formatString)) {
            return false;
        } else {
            String fs = formatString;
            int length = formatString.length();
            StringBuilder sb = new StringBuilder(length);

            int separatorIndex;
            for (separatorIndex = 0; separatorIndex < length; ++separatorIndex) {
                char c = fs.charAt(separatorIndex);
                if (separatorIndex < length - 1) {
                    char nc = fs.charAt(separatorIndex + 1);
                    if (c == '\\') {
                        switch (nc) {
                            case ' ':
                            case ',':
                            case '-':
                            case '.':
                            case '\\':
                                continue;
                        }
                    } else if (c == ';' && nc == '@') {
                        ++separatorIndex;
                        continue;
                    }
                }

                sb.append(c);
            }

            fs = sb.toString();
            if (date_ptrn4.matcher(fs).matches()) {
                return true;
            } else {
                fs = date_ptrn5.matcher(fs).replaceAll("");
                fs = date_ptrn1.matcher(fs).replaceAll("");
                fs = date_ptrn2.matcher(fs).replaceAll("");
                separatorIndex = fs.indexOf(59);
                if (0 < separatorIndex && separatorIndex < fs.length() - 1) {
                    fs = fs.substring(0, separatorIndex);
                }

                if (!date_ptrn3a.matcher(fs).find()) {
                    return false;
                } else {
                    boolean result = date_ptrn3b.matcher(fs).matches();
                    if (result) {
                        return true;
                    } else {
                        result = date_ptrn6.matcher(fs).find();
                        return result;
                    }
                }
            }
        }
    }

    public static boolean isInternalDateFormat(int format) {
        switch (format) {
            case 14:
            case 15:
            case 16:
            case 17:
            case 18:
            case 19:
            case 20:
            case 21:
            case 22:
            case 27:
            case 28:
            case 29:
            case 30:
            case 31:
            case 32:
            case 33:
            case 34:
            case 35:
            case 36:
            case 45:
            case 46:
            case 47:
            case 50:
            case 51:
            case 52:
            case 53:
            case 54:
            case 55:
            case 56:
            case 57:
            case 58:
                return true;
            case 23:
            case 24:
            case 25:
            case 26:
            case 37:
            case 38:
            case 39:
            case 40:
            case 41:
            case 42:
            case 43:
            case 44:
            case 48:
            case 49:
            default:
                return false;
        }
    }

    public static void removeThreadLocalCache() {
        DATE_THREAD_LOCAL.remove();
        DATE_FORMAT_THREAD_LOCAL.remove();
    }

    /**
     * date2比date1多的天数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDays(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        if (year1 != year2) {//不同年
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {    //闰年
                    timeDistance += 366;
                } else {  //不是闰年
                    timeDistance += 365;
                }
            }

            return timeDistance + (day2 - day1);
        } else {// 同一年
            System.out.println("判断day2 - day1 : " + (day2 - day1));
            return day2 - day1;
        }
    }
}
  