package cn.chnmuseum.party.common.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 日期工具类 - 基于LocalDateTime
 */
public class DateUtil {

    // 格式:年-月-日 小时:分钟:秒
    public static final String FORMAT_ONE = "yyyy-MM-dd HH:mm:ss";

    public static final String FORMAT_T = "yyyy-MM-dd'T'HH:mm:ss";

    // 格式:年-月-日 小时:分钟
    public static final String FORMAT_TWO = "yyyy-MM-dd HH:mm";

    // 格式:年-月-日
    public static final String LONG_DATE_FORMAT = "yyyy-MM-dd";

    // 格式:月-日
    public static final String SHORT_DATE_FORMAT = "MM-dd";

    // 格式:小时:分钟:秒
    public static final String LONG_TIME_FORMAT = "HH:mm:ss";

    // 格式:小时:分钟:秒
    public static final String SHORT_TIME_FORMAT = "HH:mm";

    // 格式:年-月
    public static final String MONTG_DATE_FORMAT = "yyyy-MM";

    // 格式:年-月
    public static final String Chinese_DATE_FORMAT = "yyyy年MM月dd日 HH:mm";

    private static final ConcurrentMap<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>();

    private static final int PATTERN_CACHE_SIZE = 500;

    /**
     * Date转换为格式化时间
     *
     * @param date    date
     * @param pattern 格式
     * @return
     */
    public static String format(Date date, String pattern) {
        return format(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), pattern);
    }

    /**
     * localDateTime转换为格式化时间
     *
     * @param localDateTime localDateTime
     * @param pattern       格式
     * @return
     */
    public static String format(LocalDateTime localDateTime, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return localDateTime.format(formatter);
    }

    /**
     * localDateTime转换为格式化时间
     *
     * @param localDateTime localDateTime
     * @return
     */
    public static String format(LocalDateTime localDateTime) {
        DateTimeFormatter formatter = createCacheFormatter(FORMAT_ONE);
        return localDateTime.format(formatter);
    }

    /**
     * 格式化字符串转为Date
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return
     */
    public static Date parseDate(String time, String pattern) {
        return Date.from(parseLocalDateTime(time, pattern).atZone(ZoneId.systemDefault()).toInstant());

    }

    /**
     * 格式化字符串转为LocalDateTime
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return
     */
    public static LocalDateTime parseLocalDateTime(String time, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 格式化字符串转为LocalDateTime
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return localDateTime
     */
    public static LocalDateTime transferLocalDateTime(String time, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return LocalDate.parse(time, formatter).atStartOfDay();
    }

    /**
     * java.util.Date --> java.time.LocalDateTime
     */
    public static LocalDateTime UDateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 将LocalDateTime转为自定义的时间格式的字符串
     *
     * @param localDateTime
     * @param format
     * @return
     */
    public static String getDateTimeAsString(LocalDateTime localDateTime, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return localDateTime.format(formatter);
    }

    /**
     * 将long类型的timestamp转为LocalDateTime
     *
     * @param timestamp
     * @return
     */
    public static LocalDateTime getDateTimeOfTimestamp(long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 将LocalDateTime转为long类型的timestamp
     *
     * @param localDateTime
     * @return
     */
    public static long getTimestampOfDateTime(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    /**
     * 将某时间字符串转为自定义时间格式的LocalDateTime
     *
     * @param time
     * @param format
     * @return
     */
    public static LocalDateTime parseStringToDateTime(String time, String format) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format, Locale.getDefault());
        return LocalDateTime.parse(time, df);
    }

    /**
     * 计算两个日期之间相差的时间
     *
     * @param startDate 较小的时间
     * @param endDate   较大的时间
     * @return 相差结果 X天X小时X分钟X秒
     */
    public static String differenceTimeStr(Date startDate, Date endDate) {
        // 计算时间差
        long between = endDate.toInstant().toEpochMilli() - startDate.toInstant().toEpochMilli();
        if (between < 0) {
            return "-1";
        }
        long day = between / (1000 * 3600 * 24);    // 天
        long hour = (between / (60 * 60 * 1000) - day * 24);    // 小时
        long min = ((between / (60 * 1000)) - day * 24 * 60 - hour * 60);   // 分钟
        long sec = (between / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);   // 秒

        StringBuilder sb = new StringBuilder();
        if (0 != day) {
            sb.append(day);
            sb.append("天");
        }
        if (0 != hour) {
            sb.append(hour);
            sb.append("小时");
        }
        if (0 != min) {
            sb.append(min);
            sb.append("分");
        }
        sb.append(sec);
        sb.append("秒");
        return String.valueOf(sb);
    }

    /**
     * 时间戳转时间【毫秒】
     *
     * @param timeStamp 时间戳【毫秒】
     * @param pattern   格式化参数
     * @return 格式化后的时间
     */
    public static String msTimeStampConvertTime(long timeStamp, String pattern) {
        Instant instant = Instant.ofEpochMilli(timeStamp);
        ZoneId zone = ZoneId.systemDefault();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.ofInstant(instant, zone).format(formatter);
    }

    /**
     * 时间戳转时间【秒】
     *
     * @param timeStamp 时间戳【秒】
     * @param pattern   格式化参数
     * @return 格式化后的时间
     */
    public static String secondsTimeStampConvertTime(long timeStamp, String pattern) {
        Instant instant = Instant.ofEpochSecond(timeStamp);
        ZoneId zone = ZoneId.systemDefault();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.ofInstant(instant, zone).format(formatter);
    }

    /**
     * 将某时间字符串转为自定义时间格式的LocalDate
     *
     * @param time
     * @param format
     * @return
     */
    public static LocalDate parseStringToDate(String time, String format) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format, Locale.getDefault());
        return LocalDate.parse(time, df);
    }

    /**
     * 将某时间字符串转为自定义时间格式的LocalDate
     *
     * @param time
     * @param format
     * @return
     */
    public static LocalTime parseStringToTime(String time, String format) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format, Locale.getDefault());
        return LocalTime.parse(time, df);
    }

    /**
     * 在缓存中创建DateTimeFormatter
     *
     * @param pattern 格式
     * @return
     */
    private static DateTimeFormatter createCacheFormatter(String pattern) {
        if (pattern == null || pattern.length() == 0) {
            throw new IllegalArgumentException("Invalid pattern specification");
        }
        DateTimeFormatter formatter = FORMATTER_CACHE.get(pattern);
        if (formatter == null) {
            if (FORMATTER_CACHE.size() < PATTERN_CACHE_SIZE) {
                formatter = DateTimeFormatter.ofPattern(pattern);
                DateTimeFormatter oldFormatter = FORMATTER_CACHE.putIfAbsent(pattern, formatter);
                if (oldFormatter != null) {
                    formatter = oldFormatter;
                }
            }
        }
        return formatter;
    }

    /**
     * 获取某个时间段的开始时间
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime minuteStart(int offset) {
        return LocalDateTime.now().plusMinutes(offset).withSecond(0).withNano(0);
    }

    /**
     * 获取某个时间段的开始时间
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime minuteEnd(int offset) {
        return LocalDateTime.now().plusMinutes(offset).withSecond(59).withNano(999999999);
    }

    /**
     * 获取某个时间段的开始时间
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime hourStart(int offset) {
        return LocalDateTime.now().plusHours(offset).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取某个时间段的结束时间
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime hourEnd(int offset) {
        return LocalDateTime.now().plusHours(offset).withMinute(59)
                .withSecond(59)
                .withNano(999999999);
    }

    /**
     * 获取某天的开始日期
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime dayStart(int offset) {
        return LocalDate.now().plusDays(offset).atStartOfDay();
    }

    /**
     * 获取日期的结束时间
     */
    public static LocalDateTime getDayStart(int offset) {
        return LocalDateTime.now().plusDays(offset).withHour(0)
                .withMinute(0)
                .withSecond(0)
                .withNano(0);
    }

    /**
     * 获取某天的开始日期
     *
     * @param offset 0今天,1明天,-1昨天,依次类推
     * @return
     */
    public static LocalDateTime dayEnd(int offset) {
        return LocalDateTime.of(LocalDate.now().plusDays(offset), LocalTime.MAX);//当天零点
    }

    /**
     * 获取日期的结束时间
     */
    public static LocalDateTime getDayEnd(int offset) {
        return LocalDateTime.now().plusDays(offset).withHour(23)
                .withMinute(59)
                .withSecond(59)
                .withNano(999999999);
    }

    /**
     * 获取此刻与相对当天第day天的起始时间相隔的秒数。day为0表示今天的起始时间;1明天,2后天,-1昨天,-2前天等,依次例推。
     *
     * @param day
     * @return
     */
    public static int ttl(int day) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime time = LocalDate.now().plusDays(day).atStartOfDay();
        return (int) Duration.between(now, time).toMillis() / 1000;
    }

    /**
     * 获取某周的开始日期
     *
     * @param offset 0本周,1下周,-1上周,依次类推
     * @return
     */
    public static LocalDateTime weekStart(int offset) {
        return LocalDateTime.now().plusWeeks(offset).with(DayOfWeek.MONDAY);
    }

    /**
     * 获取某周的开始日期
     *
     * @param offset 0本周,1下周,-1上周,依次类推
     * @return
     */
    public static LocalDateTime weekEnd(int offset) {
        return LocalDateTime.now().plusWeeks(offset).with(DayOfWeek.SUNDAY);
    }

    /**
     * 获取某月的开始日期
     *
     * @param offset 0本月,1下个月,-1上个月,依次类推
     * @return
     */
    public static LocalDateTime monthStart(int offset) {
        return LocalDateTime.now().plusMonths(offset).with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0);
    }

    /**
     * 获取某月的结束日期
     *
     * @param offset 0本月,1下个月,-1上个月,依次类推
     * @return
     */
    public static LocalDateTime monthEnd(int offset) {
        return LocalDateTime.now().plusMonths(offset).with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(999999999);
    }

    /**
     * 获取某季度的开始日期
     * 季度一年四季, 第一季度:2月-4月, 第二季度:5月-7月, 第三季度:8月-10月, 第四季度:11月-1月
     *
     * @param offset 0本季度,1下个季度,-1上个季度,依次类推
     * @return
     */
    public static LocalDate quarterStart(int offset) {
        final LocalDate date = LocalDate.now().plusMonths(offset * 3);
        int month = date.getMonth().getValue();//当月
        int start = 0;
        if (month >= 2 && month <= 4) {//第一季度
            start = 2;
        } else if (month >= 5 && month <= 7) {//第二季度
            start = 5;
        } else if (month >= 8 && month <= 10) {//第三季度
            start = 8;
        } else if (month >= 11) {//第四季度
            start = 11;
        } else {//第四季度
            start = 11;
            month = 13;
        }
        return date.plusMonths(start - month).with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取某年的开始日期
     *
     * @param offset 0今年,1明年,-1去年,依次类推
     * @return
     */
    public static LocalDateTime yearStart(int offset) {
        return LocalDateTime.now().plusYears(offset).with(TemporalAdjusters.firstDayOfYear());
    }

    /**
     * 获取某年的结束日期
     *
     * @param offset 0今年,1明年,-1去年,依次类推
     * @return
     */
    public static LocalDateTime yearEnd(int offset) {
        return LocalDateTime.now().plusYears(offset).with(TemporalAdjusters.lastDayOfYear());
    }

    /**
     * 计算两个时间相差天数
     *
     * @param day1 时间1
     * @param day2 时间2
     * @return 相差天数
     */
    public static long intervalDay(LocalDateTime day1, LocalDateTime day2) {
        return Duration.between(day1, day2).toDays();
    }

    /**
     * 计算两个时间相差天数
     *
     * @param day1   格式化的时间1
     * @param day2   格式化的时间2
     * @param format 格式化参数
     * @return 相差天数
     */
    public static long intervalDay(String day1, String day2, String format) {
        return intervalDay(transferLocalDateTime(day1, format), transferLocalDateTime(day2, format));
    }

    /**
     * 获取当前日期
     *
     * @param format 格式化参数
     * @return 格式化后的日期
     */
    public static String getCurrentDate(String format) {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));
    }
}