package com.ssm.manage.utils;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;

import javax.validation.constraints.NotNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

public class DateUtils {

    private static final String yMdHmsSFormat = "yyyyMMddHHmmssSSS";
    private static final String yMdHmsFormat = "yyyyMMddHHmmss";
    private static final String ymdhmsFormat = "yyyy-MM-dd HH:mm:ss";

    private static final String chineseFormat = "yyyy年MM月dd日 HH:mm:ss";

    private static final String ymdhmFormat = "yyyy-MM-dd HH:mm";

    private static final String ymdFormat = "yyyy-MM-dd";

    private static final String ymFormat = "yyyy-MM";

    private static final String YMDFORMAT = "yyyyMMdd";

    private static final String englishFormat = "MMM d, yyyy HH:mm:ss";

    private static final String yearFormat = "yyyy";

    private static final String ChineseYMFormat = "yyyy年MM月";

    private static final String YMFormat = "yyyyMM";

    private static final String cronFormat = "ss mm HH dd MM ? yyyy";

    private static final String utcFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    private static final String hmdFormat = "HH:mm:ss";

    /**
     * 比较两个 {@link Date} 对象之间的大小
     * @param date1 可为 {@code null}，为 {@code null} 时表示无穷小时间
     * @param date2 可为 {@code null}，为 {@code null} 时表示无穷小时间
     * @return
     * <ul>
     *     <li>当 {@code date1} 在 {@code date2} 之后，或者 {@code date1} 不为 {@code null} 且 {@code date2} 为 {@code null} 时，返回一个大于 {@code 0} 的值</li>
     *     <li>当 {@code date1} 在 {@code date2} 之前，或者 {@code date1} 为 {@code null} 且 {@code date2} 不为 {@code null} 时，返回一个小于 {@code 0} 的值</li>
     *     <li>当 {@code date1} 和 {@code date2} 相等，（包括 {@code date1} 和 {@code date2} 均为 {@code null}的情况）时，返回 {@code 0}</li>
     * </ul>
     */
    public static int compareDateTime(Date date1, Date date2) {
        return date1 == null ? (date2 == null ? 0 : -1) : (date2 == null ? 1 : date1.compareTo(date2));
    }

    /**
     * 比较两个 {@link Date} 对象的日期部分之间的大小
     * @param date1 可为 {@code null}，为 {@code null} 时表示无穷小时间
     * @param date2 可为 {@code null}，为 {@code null} 时表示无穷小时间
     * @return
     * <ul>
     *     <li>当 {@code date1} 在 {@code date2} 之后，或者 {@code date1} 不为 {@code null} 且 {@code date2} 为 {@code null} 时，返回一个大于 {@code 0} 的值</li>
     *     <li>当 {@code date1} 在 {@code date2} 之前，或者 {@code date1} 为 {@code null} 且 {@code date2} 不为 {@code null} 时，返回一个小于 {@code 0} 的值</li>
     *     <li>当 {@code date1} 和 {@code date2} 相等，（包括 {@code date1} 和 {@code date2} 均为 {@code null}的情况）时，返回 {@code 0}</li>
     * </ul>
     * <p>
     *     NOTE: 在对比时仅对比 {@link Date} 对象的日期部分（年月日）的值，具体时间（时分秒）不参与比较
     * </p>
     */
    public static int compareDate(Date date1, Date date2) {
        // 处理日期对象为空的情况
        if (date1 == null && date2 == null) {
            return 0; // 两个日期都为空，认为相等
        } else if (date1 == null) {
            return -1; // date1 为空，认为 date1 在 date2 之前（即小于）
        } else if (date2 == null) {
            return 1; // date2 为空，认为 date1 在 date2 之后（即大于）
        }

        // 转换为 Instant 对象
        Instant instant1 = date1.toInstant();
        Instant instant2 = date2.toInstant();

        // 计算两个 Instant 对象之间的时间间隔
        Duration duration = Duration.between(instant1, instant2);

        long dayDifference = duration.toDays();
        return dayDifference > 0 ? 1 : (dayDifference < 0 ? -1 : 0);
    }

    /**
     * 获取当前 {@link Date} 23:59:00.000 这个时间点, 延后 {@code days} 天的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param days 延后的天数, 必须为非负整数
     */
    public static Date getEndOfDayAfter(@NotNull Date current, int days) {
        Calendar calendar = getCalendarByTime(current, 23, 59, 0, 0);

        if (days > 0) {
            // 将日期增加指定天数
            calendar.add(Calendar.DAY_OF_YEAR, days);
        }

        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    /**
     * 获取当前 {@link Date} 指定时间点的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param hour 为 {@link Date} 设置的小时数, 必须大于等于 0, 小于 24
     * @return 如果参数不满足要求, 那么它对应的部分将保留为参数 {@code current} 原本的值, 分钟、秒和毫秒部分将被设置为 {@code 0}
     */
    public static Date getDateByTime(@NotNull Date current, int hour) {
        Calendar calendar = getCalendarByTime(current, hour, 0, 0, 0);
        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    /**
     * 获取当前 {@link Date} 指定时间点的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param hour 为 {@link Date} 设置的小时数, 必须大于等于 0, 小于 24
     * @param minute 为 {@link Date} 设置的分钟数, 必须大于等于 0, 小于 60
     * @return 如果参数不满足要求, 那么它对应的部分将保留为参数 {@code current} 原本的值, 秒和毫秒部分将被设置为 {@code 0}
     */
    public static Date getDateByTime(@NotNull Date current, int hour, int minute) {
        Calendar calendar = getCalendarByTime(current, hour, minute, 0, 0);
        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    /**
     * 获取当前 {@link Date} 指定时间点的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param hour 为 {@link Date} 设置的小时数, 必须大于等于 0, 小于 24
     * @param minute 为 {@link Date} 设置的分钟数, 必须大于等于 0, 小于 60
     * @param second 为 {@link Date} 设置的秒数, 必须大于等于 0, 小于 60
     * @return 如果参数不满足要求, 那么它对应的部分将保留为参数 {@code current} 原本的值, 毫秒部分将被设置为 {@code 0}
     */
    public static Date getDateByTime(@NotNull Date current, int hour, int minute, int second) {
        Calendar calendar = getCalendarByTime(current, hour, minute, second, 0);
        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    /**
     * 获取当前 {@link Date} 指定时间点的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param hour 为 {@link Date} 设置的小时数, 必须大于等于 0, 小于 24
     * @param minute 为 {@link Date} 设置的分钟数, 必须大于等于 0, 小于 60
     * @param second 为 {@link Date} 设置的秒数, 必须大于等于 0, 小于 60
     * @param millisecond 为 {@link Date} 设置的毫秒数, 必须大于等于 0, 小于 1000
     * @return 如果参数不满足要求, 那么它对应的部分将保留为参数 {@code current} 原本的值
     */
    public static Date getDateByTime(@NotNull Date current, int hour, int minute, int second, int millisecond) {
        Calendar calendar = getCalendarByTime(current, hour, minute, second, millisecond);
        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    /**
     * 获取当前 {@link Date} 指定时间点的 {@link Calendar} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param hour 为 {@link Date} 设置的小时数, 必须大于等于 0, 小于 24
     * @param minute 为 {@link Date} 设置的分钟数, 必须大于等于 0, 小于 60
     * @param second 为 {@link Date} 设置的秒数, 必须大于等于 0, 小于 60
     * @param millisecond 为 {@link Date} 设置的毫秒数, 必须大于等于 0, 小于 1000
     * @return 如果参数不满足要求, 那么它对应的部分将保留为参数 {@code current} 原本的值
     */
    public static Calendar getCalendarByTime(@NotNull Date current, int hour, int minute, int second, int millisecond) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(current);

        // 设置具体时间
        if (hour >= 0 && hour < 24) {
            calendar.set(Calendar.HOUR_OF_DAY, hour);
        }
        if (minute >= 0 && minute < 60) {
            calendar.set(Calendar.MINUTE, minute);
        }
        if (second >= 0 && second < 60) {
            calendar.set(Calendar.SECOND, second);
        }
        if (millisecond >= 0 && millisecond < 1000) {
            calendar.set(Calendar.MILLISECOND, millisecond);
        }

        return calendar;
    }

    /**
     * 获取当前 {@link Date} 延后 {@code days} 天的 {@link Date} 对象
     * @param current 当前时间日期的 {@link Date} 对象
     * @param days 延后的天数, 必须为正整数
     */
    public static Date getDelayedDate(@NotNull Date current, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(current);

        if (days > 0) {
            // 将日期增加指定天数
            calendar.add(Calendar.DAY_OF_YEAR, days);
        }

        // 获取修改后的 Date 对象
        return calendar.getTime();
    }

    public static Date getNextDaySameTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }
    public static int compare(Date one, Date two) {
        Long num1 = one.getTime();
        Long num2 = two.getTime();
        return (int) ((num2 - num1) / (24 * 3600 * 1000));
    }

    public static int compare(String str1, String str2) {
        Date one = null;
        Date two = null;
        try {
            one = DateUtils.ymdString2Date(str1);
            two = DateUtils.ymdString2Date(str2);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return compare(one, two);
    }

    public static String YMDFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, YMDFORMAT);
    }

    public static Date YMDParse(String date) throws ParseException {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(YMDFORMAT).parse(date);
    }

    public static String YMFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, YMFormat);
    }

    public static Date YMParse(String date) throws ParseException {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(YMFormat).parse(date);
    }

    public static Date ymParse(String date) throws ParseException {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(ymFormat).parse(date);
    }

    public static String yearFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, yearFormat);
    }



    public static String ymdhmsSFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, yMdHmsSFormat);
    }

    public static String ChineseYMFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, ChineseYMFormat);
    }


    public static String ymdhmsFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, ymdhmsFormat);
    }

    public static String chineseFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, chineseFormat);
    }

    public static String ymdhmFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, ymdhmFormat);
    }

    public static String ymdFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, ymdFormat);
    }

    public static String ymFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, ymFormat);
    }

    public static List<String> ymdBetweenFormat(Date d1, Date d2) {
        List<String> list = new ArrayList<>();
        if (d1 == null || d2 == null) {
            return list;
        }
        Date da1;
        Date da2;
        if (d1.getTime() < d2.getTime()) {
            da1 = d1;
            da2 = d2;
        } else {
            da1 = d2;
            da2 = d1;

        }
        for (Date d = d1; getDayDiff(d, d2) >= 0; d = diffDay(d, 1)) {
            list.add(ymdFormat(d));
        }
        return list;
    }

    public static Date ymdString2Date(String ymdStringDate)
            throws ParseException {
        if (ymdStringDate == null) {
            return null;
        }
        return new SimpleDateFormat(ymdFormat).parse(ymdStringDate);
    }

    public static Date ymdhmsString2DateTime(String ymdhmsStringDate)
            throws ParseException {
        if (ymdhmsStringDate == null) {
            return null;
        }
        return new SimpleDateFormat(ymdhmsFormat).parse(ymdhmsStringDate);
    }

    public static Date utc2Date(String utc) throws ParseException {

        if (StringUtils.isBlank(utc)) {
            return null;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(utcFormat);
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return simpleDateFormat.parse(utc);
    }

    public static String utcFormat(Date date) {
        if (date == null) {
            return "";
        }
        return DateFormatUtils.format(date, utcFormat);
    }


    static public long getSecondDiff(Date d1, Date d2) {
        long d1t = d1.getTime();
        long d2t = d2.getTime();
        return (d2t - d1t) / 1000;
    }

    /**
     * 获取两天的时间差 单位天
     *
     * @param d1 前
     * @param d2 后
     * @return d2-d1
     */
    static public long getDayDiff(Date d1, Date d2) {
        long d1t = d1.getTime();
        long d2t = d2.getTime();
        return (d2t - d1t) / (1000 * 60 * 60 * 24);
    }

    static public long getHourDiff(Date d1, Date d2) {
        long d1t = d1.getTime();
        long d2t = d2.getTime();
        return (d2t - d1t) / (1000 * 60 * 60);
    }

    static public Long getDateNowLong() {
        return new Long(DateFormatUtils.format(new Date(), yMdHmsFormat));
    }

    static public String getDateNowStr() {
        return getDate2Str(new Date());
    }

    static public String getDate2Str(Date date) {
        return DateFormatUtils.format(date, yMdHmsFormat);
    }

    public static Date getStr2Date(String str) throws ParseException {
        if (str == null || str.length() != 14) {
            return null;
        }
        return new SimpleDateFormat(yMdHmsFormat).parse(str);
    }

    /**
     * 时间查询时,结束时间的 23:59:59
     */
    public static String addDateEndPostfix(String datestring) {
        if (datestring == null || datestring.equals("")) {
            return null;
        }
        return datestring + " 23:59:59.998";
    }

    /**
     * 时间查询时,结束时间的 00:00:00
     */
    public static String addDateBeginPostfix(String datestring) {
        if (datestring == null || datestring.equals("")) {
            return null;
        }
        return datestring + " 00:00:00";
    }


    /**
     * 取当前时间的格式：如20091112
     *
     * @return 日期字符串
     */
    public static String getDateNowStrShort() {
        return YMDFormat(new Date());
    }

    public static Date englishFormatStr2Date(String date) {
        try {
            return new SimpleDateFormat(englishFormat, Locale.ENGLISH).parse(date);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 取当前年：如2016
     *
     * @return
     */
    public static String getCurrentYear() {
        return DateFormatUtils.format(new Date(), yearFormat);
    }

    /**
     * 取当前年月：如201605
     *
     * @return
     */
    public static String getCurrentYearMonth() {
        return DateFormatUtils.format(new Date(), YMFormat);
    }

    /**
     * 获取当前年的第几天
     */
    public static int getDayCurrentNum() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        return calendar.get(Calendar.DAY_OF_YEAR);
    }


    public static Date getExpectDate(Date expectDate, Date expectTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(expectTime);
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(expectDate);
        calendar.set(calendar1.get(Calendar.YEAR), calendar1.get(Calendar.MONTH), calendar1.get(Calendar.DATE));
        return calendar.getTime();
    }

    public static Date diffMonth(Date date, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, amount);
        return calendar.getTime();
    }

    public static Date diffDay(Date date, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, amount);
        return calendar.getTime();
    }

    public static Date diffHour(Date date, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, amount);
        return calendar.getTime();
    }

    public static String date2Cron(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(cronFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = simpleDateFormat.format(date);
        }
        return formatTimeStr;
    }

    /**
     * 获得该月第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        date = cal.getTime();
        return date;
    }

    /**
     * 获取该月最后一天
     *
     * @param date
     * @return
     */
    public static Date getEndDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, 1);
        cal.add(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
    }
    public static boolean isTheSameYear(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
    }

    public static boolean isTheSameMonth(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) && c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
    }

    public static boolean isTheSameDay(Date d1,Date d2){
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) && c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH) && c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH);
    }

    public static int getMonthFromDate(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.MONTH);
    }

    public static Date nextMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MONTH, 1);
        return c.getTime();
    }

    /**
     * 判断时间是否在时间段内
     *
     * @param nowTime Date
     * @param begin   HH:mm:ss
     * @param end     HH:mm:ss
     * @return
     */
    public static boolean belongCalendar(Date nowTime, String begin, String end) throws ParseException {
        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);
        SimpleDateFormat sdf = new SimpleDateFormat(hmdFormat);
        Date beginTime = sdf.parse(begin);
        Calendar beginC = Calendar.getInstance();
        beginC.setTime(beginTime);
        Date endTime = sdf.parse(end);
        Calendar endC = Calendar.getInstance();
        endC.setTime(endTime);

        if (date.after(beginC) && date.before(endC)) {
            return true;
        } else {
            return false;
        }
    }
}