package com.liquidnet.commons.lang.util;

import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

public abstract class DateUtil {
    public static final String DATE_FULL_STR = "yyyy-MM-dd HH:mm:ss";
    public static final String DATEFULLSTR = "yyyyMMddHHmmss";
    public static final String DATE_SMALL_STR = "yyyy-MM-dd";

    public static final SimpleDateFormat SDF_YMD_HMS = new SimpleDateFormat(DATE_FULL_STR);
    public static final SimpleDateFormat SDF_YMD = new SimpleDateFormat(DATE_SMALL_STR);
    public static final DateTimeFormatter DTF_YMD_HMS = DateTimeFormatter.ofPattern(DATE_FULL_STR);
    public static final DateTimeFormatter DTFYMDHMS = DateTimeFormatter.ofPattern(DATEFULLSTR);

    /**
     * 日期格式化器
     */
    public enum Formatter {

        MM_dd("MM-dd"),
        yyyyMMdd("yyyyMMdd"),
        yyyyMM("yyyyMM"),
        HHmm("HH:mm"),
        yyyy_MM_dd("yyyy-MM-dd"),
        yyyy_MM_dd2("yyyy/MM/dd"),
        yyyyMMddHH("yyyy-MM-dd HH"),
        yyyyMMddHHmm("yyyy-MM-dd HH:mm"),
        yyyyMMddHHmmss("yyyy-MM-dd HH:mm:ss"),
        yyyyMMddHHmmssTrim("yyyyMMddHHmmss"),
        yyyyMMddHHmmssSSS("yyyyMMddHHmmssSSS"),
        yyyyMMddHHmmssSSSUnTrim("yyyy-MM-dd HH:mm:ss:SSS"),
        yyyyMMddHHmmssS("yyyy-MM-dd HH:mm:ss.S"),
        ddHHmmssTrim("ddHHmmss"),
        yyyy_MM_dd_zh("yyyy年MM月dd日"),
        MM_dd_zh("MM月dd日"),
        english("d MMM yyyy HH:mm 'UTC'", Locale.ENGLISH),
        ;

        /**
         * 线程安全,可共享
         */
        private DateTimeFormatter formatter;

        Formatter(String pattern) {
            this.formatter = DateTimeFormatter.ofPattern(pattern);
        }

        Formatter(String pattern, Locale language) {
            this.formatter = DateTimeFormatter.ofPattern(pattern, language);
        }

        public String format(LocalDateTime localDateTime) {
            if (localDateTime == null) {
                return null;
            }
            return formatter.format(localDateTime);
        }

        public String format(LocalDate localDate) {
            if (localDate == null) {
                return null;
            }
            return formatter.format(localDate);
        }

        public LocalDateTime parse(String str) {
            if (null == str || str.trim().length() <= 0) {
                return null;
            }
            return LocalDateTime.parse(str, formatter);
        }

    }

    /**
     * 获取当前时间
     */
    public static Date now() {
        return new Date();
    }

    /**
     * 计算日期，增加月数
     */
    public static Date addMonth(Date date, int months) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.MONTH, c.get(Calendar.MONTH) + months);
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，增加年数
     */
    public static Date addYear(Date date, int years) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.YEAR, c.get(Calendar.YEAR) + years);

        LocalDate ldate = LocalDate.now();
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，增加天数
     */
    public static Date addDay(Date date, int days) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) + days);
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，增加天数
     */
    public static String addDay(String date, int days) {
        Calendar c = Calendar.getInstance();
        c.setTime(parse(date, "yyyy-MM-dd HH:mm:ss"));
        c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) + days);
        return format(new Date(c.getTimeInMillis()), Formatter.yyyyMMddHHmmss);
    }

    /**
     * 计算日期，增加小时
     */
    public static Date addHour(Date date, int hours) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + hours);
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，增加分支
     */
    public static Date addMin(Date date, int months) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + months);
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，增加天数
     *
     * @param holidays 不包含的节假日
     */
    public static Date addDay(Date date, int days, Set<String> holidays) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int i = 0;
        while (i < days) {
            c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) + 1);
            if (holidays.contains(format(c.getTime(), Formatter.MM_dd))) {
                continue;
            }
            i++;
        }
        return new Date(c.getTimeInMillis());
    }

    /**
     * 计算日期，减少天数
     *
     * @param holidays 不包含的节假日
     */
    public static Date reduceDay(Date date, int days, Set<String> holidays) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int i = 0;
        while (i < days) {
            c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) - 1);
            if (holidays.contains(format(c.getTime(), Formatter.MM_dd))) {
                continue;
            }
            i++;
        }
        return new Date(c.getTimeInMillis());
    }

    /**
     * 根据指定的格式,格式化日期
     *
     * @param date      java8中的日期
     * @param formatter 格式化
     * @return 格式化字符串
     */
    public static String format(LocalDateTime date, Formatter formatter) {
        return formatter.format(date);
    }

    public static String format(LocalDate date, Formatter formatter) {
        return formatter.format(date);
    }

    /**
     * 根据指定的格式,格式化日期
     *
     * @param date      java8 之前的传统日期
     * @param formatter 格式化
     * @return 格式化字符串
     */
    public static String format(Date date, Formatter formatter) {
        return format(asLocalDateTime(date), formatter);
    }

    /**
     * 格式化当前时间
     *
     * @param formatter 格式化
     * @return 当前时间的格式化字符串
     */
    public static String format(Formatter formatter) {
        return format(LocalDateTime.now(), formatter);
    }

    /**
     * 使用参数Format将字符串转为Date
     */
    public static Date parse(String strDate, String pattern) {
        try {
            return StringUtils.isBlank(strDate) ? null : new SimpleDateFormat(
                    pattern).parse(strDate);
        } catch (ParseException e) {
            return null;
        }
    }

    public static Date asDate(LocalDate localDate) {
        return localDate == null ? null : Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date asDate(LocalDateTime localDateTime) {
        return localDateTime == null ? null : Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static LocalDate asLocalDate(Date date) {
        return date == null ? null : Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDateTime asLocalDateTime(Date date) {
        return date == null ? null : Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 获取指定年份的所有周末(周六,周日)
     */
    public static List<LocalDate> getWeekend(int year) {
        LocalDate localDate = LocalDate.of(year, 1, 1);
        List<LocalDate> result = new ArrayList<>();
        LocalDate today = localDate;
        int i;
        do {
            switch (today.getDayOfWeek()) {
                case SATURDAY:
                    result.add(today);
                    today = today.plusDays(1);
                    if (today.getYear() == year) {
                        result.add(today);
                    }
                    i = 6;
                    break;
                case SUNDAY:
                    result.add(today);
                    i = 6;
                    break;
                default:
                    i = 1;
                    break;
            }
            today = today.plusDays(i);
        } while (today.getYear() == year);

        return result;
    }

    /**
     * 比较日期
     */
    public static int compareDay(Date date1, Date date2) {
        date1 = parse(format(date1, Formatter.yyyyMMdd), Formatter.yyyyMMdd.toString());
        date2 = parse(format(date2, Formatter.yyyyMMdd), Formatter.yyyyMMdd.toString());
        return date1.compareTo(date2);
    }

    /**
     * 比较月份
     */
    public static int compareMonth(Date date1, Date date2) {
        date1 = parse(format(date1, Formatter.yyyyMM), Formatter.yyyyMM.toString());
        date2 = parse(format(date2, Formatter.yyyyMM), Formatter.yyyyMM.toString());
        return date1.compareTo(date2);
    }

    /**
     * 比较日期
     */
    public static int compareStrDay(String date1, String date2) {
        LocalDateTime date1L = LocalDateTime.parse(date1, DTF_YMD_HMS);
        LocalDateTime date2L = LocalDateTime.parse(date2, DTF_YMD_HMS);

        if (date1L.isAfter(date2L)) {// 大于
            return 1;
        } else if (date1L.isBefore(date2L)) { //小于
            return -1;
        } else { // 相等
            return 0;
        }
    }

    /**
     * 间隔天数
     *
     * @param date1 开始日期
     * @param date2 结束日期
     */
    public static long intervalDays(Date date1, Date date2) {
        return asLocalDate(date2).toEpochDay() - asLocalDate(date1).toEpochDay();
    }

    /**
     * 间隔天数
     *
     * @param date1 开始日期
     * @param date2 结束日期
     */
    public static long intervalDays(String date1, String date2) {
        Date date1D = parse(date1, "yyyy-MM-dd HH:mm:ss");
        Date date2D = parse(date2, "yyyy-MM-dd HH:mm:ss");
        return asLocalDate(date2D).toEpochDay() - asLocalDate(date1D).toEpochDay();
    }

    /**
     * 间隔月
     *
     * @param date1 开始日期
     * @param date2 结束日期
     */
    public static long intervalMonths(Date date1, Date date2) {
        return asLocalDate(date2).toEpochDay() - asLocalDate(date1).toEpochDay();
    }

    /**
     * 获取某天，之前或之后几天的从零点开始时间
     */
    public static Date getBeforeDayBegin(Calendar calendar, int days) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.add(Calendar.DATE, days);
        calendar.set(Calendar.MILLISECOND, 0);
        Date date = calendar.getTime();
        return date;
    }

    /**
     * 获取某天，之前或之后几天的23时，59分，59秒的时间
     */
    public static Date getBeforeDayEnd(Calendar calendar, int days) {
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        calendar.add(Calendar.DATE, days);
        Date date = calendar.getTime();
        return date;
    }

    /**
     * 间隔小时数
     *
     * @param date1 开始日期
     * @param date2 结束日期
     */
    public static long intervalHours(Date date1, Date date2) {
        long day = 0;
        try {

            day = (date1.getTime() - date2.getTime()) / (60 * 60 * 1000);

//         这里精确到了秒，我们可以在做差的时候将时间精确到天
        } catch (Exception e) {
            return 0;
        }

        return day;
    }

    /**
     * 两个日期间隔秒数
     */
    public static long intervalSeconds(Date date1, Date date2) {
        long seconds = 0;
        try {
            seconds = (date1.getTime() - date2.getTime()) / (1000);
        } catch (Exception e) {
            return 0;
        }
        return seconds;
    }

    /**
     * 返回当前时间 yyyy-MM-dd HH:mm:ss
     */
    public static String getNowTime() {
        String time = SDF_YMD_HMS.format(new Date());
        return time;
    }

    /**
     * 返回当前时间
     */
    public static String getNowTime(String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        String time = sdf.format(new Date());
        return time;
    }

    /**
     * 获取两个日期相差的月数
     *
     * @param d1 较大的日期
     * @param d2 较小的日期
     * @return 如果d1>d2返回 月数差 否则返回0
     */
    public static int getMonthDiff(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(d1);
        c2.setTime(d2);
        if (c1.getTimeInMillis() < c2.getTimeInMillis()) return 0;
        int year1 = c1.get(Calendar.YEAR);
        int year2 = c2.get(Calendar.YEAR);
        int month1 = c1.get(Calendar.MONTH);
        int month2 = c2.get(Calendar.MONTH);
        int day1 = c1.get(Calendar.DAY_OF_MONTH);
        int day2 = c2.get(Calendar.DAY_OF_MONTH);
        // 获取年的差值 假设 d1 = 2015-8-16  d2 = 2011-9-30
        int yearInterval = year1 - year2;
        // 如果 d1的 月-日 小于 d2的 月-日 那么 yearInterval-- 这样就得到了相差的年数
        if (month1 < month2 || month1 == month2 && day1 < day2) yearInterval--;
        // 获取月数差值
        int monthInterval = (month1 + 12) - month2;
        if (day1 < day2) monthInterval--;
        monthInterval %= 12;
        return yearInterval * 12 + monthInterval;
    }

    // 获得当天开始时间和结束时间 jiangxiulong
    public static HashMap oneDayStartEnd() {
        HashMap datas = new HashMap();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        Date start = calendar.getTime();
        String startStr = SDF_YMD_HMS.format(start);
        datas.put("start", start);
        datas.put("startStr", startStr);

        calendar.add(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.SECOND, -1);

        Date end = calendar.getTime();
        String endStr = SDF_YMD_HMS.format(end);
        datas.put("end", end);
        datas.put("endStr", endStr);

        return datas;
    }

    // 获取当月第一天 jiangxiulong
    public static String getMonthFirst() {
        //规定返回日期格式
        Calendar calendar = Calendar.getInstance();
        Date theDate = calendar.getTime();
        GregorianCalendar gcLast = (GregorianCalendar) Calendar.getInstance();
        gcLast.setTime(theDate);
        //设置为第一天
        gcLast.set(Calendar.DAY_OF_MONTH, 1);
        String dayFirst = SDF_YMD.format(gcLast.getTime());
        //打印本月第一天
        return dayFirst;
    }

    // 获取某月第一天 jiangxiulong
    public static String getMonthFirst(String month) {
        return month.concat("-01 00:00:00");
    }

    // 获取当月最后一天 jiangxiulong
    public static String getMonthLast() {
        //获取Calendar
        Calendar calendar = Calendar.getInstance();
        //设置日期为本月最大日期
        calendar.set(Calendar.DATE, calendar.getActualMaximum(calendar.DATE));
        //设置日期格式
        String dayLast = SDF_YMD.format(calendar.getTime());

        return dayLast + " 23:59:59";
    }

    // 获取某月最后一天 jiangxiulong
    public static String getMonthLast(String month) {
        // String 转 Date
        Date monthDate = null;
        try {
            monthDate = SDF_YMD.parse(month);
        } catch (Exception e) {
            return "";
        }
        //获取Calendar
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(monthDate);
        //设置日期为本月最大日期
        calendar.set(Calendar.DATE, calendar.getActualMaximum(calendar.DATE));
        //设置日期格式
        String dayLast = SDF_YMD.format(calendar.getTime());

        return dayLast + " 23:59:59";
    }

    /**
     * 秒级时间戳转 LocalDateTime
     *
     * @param epochMilli 秒级时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime ofEpochMilli(long epochMilli) {
//        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.of("+8"));
        return LocalDateTime.ofEpochSecond(epochMilli, 0, ZoneOffset.ofHours(8));
    }

    /**
     * 获取10位时间戳，精确到秒
     *
     * @return
     */
    public static Long getNowSeconds() {
        //获取秒数
        Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        //获取毫秒数
//        Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        return second;
    }

    public static void main(String[] args) {
        System.out.println(DateUtil.getNowTime());
        //获取秒数
        Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
//获取毫秒数
        Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();

        System.out.println("second===" + second);
        System.out.println("milliSecond===" + milliSecond);
    }
}
