package com.chushang.common.core.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.*;

/**
 * by zhaowenyuan create 2021/10/26 14:52
 * 时间工具类
 */
public class DateUtils {
    private static final DateTimeFormatter dtf =  DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
    public final static String DEFAULT_TIME_ZONE = "GMT";

    /**
     * 时间戳
     * @author ant
     * @return long
     */
    public static long getZoneOfMillis(){
        // 获取 Utc 的 localDateTime 并且将其转为时间戳
        return getZoneOfMillis(LocalDateTime.now(ZoneOffset.UTC));
    }

    public static long getZoneOfMillis(String dateTime){
        Instant instant = Instant.parse(dateTime);
        return instant.toEpochMilli();
    }

    public static long getSecondAtStartOfDay(String date){
        return getMillisAtStartOfDay(date) / 1000;
    }

    public static long getMillisAtStartOfDay(String date){
        return getMillisAtStartOfDay(parseDate(date));
    }

    public static long getSecondAtStartOfDay(LocalDate date){
        return getMillisAtStartOfDay(date) / 1000;
    }

    public static long getMillisAtStartOfDay(LocalDate date){
        return getZoneOfMillis(date.atStartOfDay());
    }

    public static long getSecondAtEndOfDay(String date){
        return getMillisAtEndOfDay(date) / 1000;
    }

    public static long getSecondAtEndOfDay(LocalDate date){
        return getMillisAtEndOfDay(date) / 1000;
    }
    public static long getMillisAtEndOfDay(String date){
        return getMillisAtEndOfDay(parseDate(date));
    }

    public static long getMillisAtEndOfDay(LocalDate date){
        return getZoneOfMillis(date.atTime(23,59,59));
    }

    public static long getZoneOfMillis(LocalDateTime localDateTime){
        return localDateTime.atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
    }

    public static long getZoneOfMillis(LocalDateTime localDateTime, ZoneOffset zoneOffset){
        return localDateTime.atZone(zoneOffset).toInstant().toEpochMilli();
    }

    public static long getMillisAtStartOfDay(LocalDate date, ZoneOffset zoneOffset){
        return getZoneOfMillis(date.atStartOfDay(), zoneOffset);
    }

    public static long getZoneOfSecond() {
        return getZoneOfMillis() / 1000;
    }

    public static long getZoneOfSecond(LocalDateTime localDateTime) {
        return getZoneOfMillis(localDateTime) / 1000;
    }
    /**
     * 获取 utc+0 的时间字符串
     */
    public static String getUtcTime(){
        return format(ZonedDateTime.now(ZoneOffset.UTC));
    }

    /**
     * 格式化
     */
    public static String format(ZonedDateTime zonedDateTime){
        return format(zonedDateTime, dtf);
    }
    /**
     * 格式化
     */
    public static String format(ZonedDateTime zonedDateTime, DateTimeFormatter formatter){
        return zonedDateTime.format(formatter);
    }

    /**
     * 格式化
     */
    public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter){
        return localDateTime.format(formatter);
    }

    /**
     * 格式化
     */
    public static String format(LocalDateTime localDateTime){
        return format(localDateTime, dtf);
    }


    /** 时间格式(yyyy-MM-dd) */
    public final static String DATE_PATTERN = "yyyy-MM-dd";

    public static LocalDate toLocalDate(long secondTimestamp){
        return Instant.ofEpochSecond(secondTimestamp).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDate parseDate(String date)
    {
        return parseDate(date,DATE_PATTERN);
    }

    public static String format(LocalDate date){
        return format(date,DATE_PATTERN);
    }

    public static String format(LocalDate date,String pattern){
        if (ObjectUtil.isNull(date)){
            return null;
        }
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
        return date.format(dtf);
    }

    public static LocalDate parseDate(String date, String pattern)
    {
        if (StringUtils.isBlank(date)){
            return null;
        }
        return LocalDate.parse(date,DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     * @param date  日期
     * @return  返回yyyy-MM-dd格式日期
     */
    public static String format(Date date) {
        return format(date, DATE_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     * @param date  日期
     * @param pattern  格式，如：DatePattern.NORM_DATETIME_PATTERN
     * @return  返回yyyy-MM-dd格式日期
     */
    public static String format(Date date, String pattern) {
        if(date != null){
            SimpleDateFormat df = new SimpleDateFormat(pattern);
            return df.format(date);
        }
        return null;
    }

    public static String format(ZonedDateTime zonedDateTime,String pattern){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return zonedDateTime.format(formatter);
    }

    /**
     * 计算日期的 区间
     * @param startDateStr 开始时间
     * @param endDateStr 结束时间
     */
    public static Set<String> dateIntervalCalculation(String startDateStr, String endDateStr)
    {
        Clock clock = Clock.system(ZoneId.of(DateUtils.DEFAULT_TIME_ZONE));
        // 开始日期
        ZonedDateTime startDateTime = parseDate(startDateStr).atStartOfDay().atZone(clock.getZone());
        // 结束日期
        ZonedDateTime endDateTime = parseDate(endDateStr).atStartOfDay().atZone(clock.getZone());
        Set<String> timeSet = new HashSet<>();
        for (ZonedDateTime start = startDateTime; endDateTime.isAfter(start); start = start.plusDays(1L)){
            timeSet.add(format(start, DateUtils.DATE_PATTERN));
        }
        // 最后需要添加一下 结束日期
        timeSet.add(format(endDateTime, DateUtils.DATE_PATTERN));
        return timeSet;
    }

    @SneakyThrows
    public static Set<String> dateIntervalCalculationByMonth(String startDateStr, String endDateStr)
    {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.NORM_MONTH_PATTERN);
        Date startDate = simpleDateFormat.parse(startDateStr);
        Date endDate = simpleDateFormat.parse(endDateStr);
        Calendar min = Calendar.getInstance();
        min.setTime(startDate);
        Set<String> timeSet = new HashSet<>();
        while (!min.getTime().after(endDate)){
            timeSet.add(simpleDateFormat.format(min.getTime()));
            min.add(Calendar.MONTH, 1);
        }
        return timeSet;
    }

    /**
     * 根据传入的日期判断 日期属于年份的第几周
     * 如果 week 小于10 , 则需要进行补0
     * date 应该是具体的实际日期 -- 即不进行获取周一，周末
     * @param date 某个指定日期
     */
    public static String weekOfYear(LocalDate date)
    {
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,4);
        int weekYear = date.getYear();
        int week = date.get(weekFields.weekOfWeekBasedYear());
        if (week < 10)
            return weekYear + "0" + week;
        return weekYear + "" + week;
    }

    public static String weekOfYearName(LocalDate date)
    {
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,4);
        int weekYear = date.getYear();
        int week = date.get(weekFields.weekOfWeekBasedYear());
        return weekYear + "年第" + week + "周";
    }

    public static String quarterOfYear(String date){
        String[] startSplit = date.split("-");
        // 开始季度
        int quarter = Integer.parseInt(startSplit[1]);
        // 开始年份
        int year = Integer.parseInt(startSplit[0]);
        return year + "年第" + quarter + "季度";
    }


    public static String formatYearMonth(String date){
        return parseDate(date).format(DateTimeFormatter.ofPattern("yyyy_MM"));
    }

    public static String formatYearMonthDay(String date){
        return parseDate(date).format(DateTimeFormatter.ofPattern("yyyy_MM_dd"));
    }
    public static String formatYearMonthDay(LocalDate date){
        return date.format(DateTimeFormatter.ofPattern("yyyy_MM_dd"));
    }

    public final static String ISO_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public final static String DATE_PATTERN_2 = "yyyyMMdd";
    public static String isoFormat(LocalDateTime dateTime)
    {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(ISO_DATE_TIME_PATTERN);
        return dateTime.format(dtf);
    }

    public static Long getSecond(String offsetId) {
        return LocalDateTime.now().toEpochSecond(ZoneOffset.of(offsetId));
    }


    public static Date getThisWeekThursday(String day){
        return getWeekDate(day);
    }

    /**
     * 获取本周4
     *
     * @param day 日期
     */
    private static Date getWeekDate(String day) {
        Date date = stringToDate(day, DATE_PATTERN);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 获得当前日期是一个星期的第几天
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        // 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        // 获得当前日期是一个星期的第几天
        int week = cal.get(Calendar.DAY_OF_WEEK);
        // 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - (week - 3));
        return cal.getTime();
    }

    /**
     * 字符串转换成日期
     * @param strDate 日期字符串
     * @param pattern 日期的格式，如：DatePattern.NORM_DATETIME_PATTERN
     */
    public static Date stringToDate(String strDate, String pattern) {
        LocalDate localDate = parseDate(strDate, pattern);
        ZoneId zone = ZoneId.systemDefault();
        assert localDate != null;
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }


    public static boolean judgeEquMonth(String startDateStr, String endDateStr) {
        if (StringUtils.isEmpty(startDateStr) || StringUtils.isEmpty(endDateStr)){
            return false;
        }
        // 均设置为1号, 进行比较
        LocalDate startDate = parseDate(startDateStr).withDayOfMonth(1);
        LocalDate endDate = parseDate(endDateStr).withDayOfMonth(1);
        return startDate.equals(endDate);
    }
}
