package com.cpp.web.utils;

import com.cpp.common.utils.DateUtils;
import com.sun.istack.NotNull;

import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 时间工具类
 *
 * @author zzy
 * @version 1.0
 * @since 2019/8/2 13:09
 */
@SuppressWarnings("WeakerAccess")
public class DateTimeUtil {

    /**
     * 获取月份
     *
     * @param dateTime 时间，单位：毫秒
     * @return 月份
     */
    public static int getMonth(@NotNull final Long dateTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(dateTime);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取指定时间在当天所处于的时刻(15分钟为一个时刻)
     * 例如：dateTime：2018-10-22 16:44:00
     * 从0点0分开始计算，0点0分-0点15为第一个时刻，依次类推该时间的时刻数为67
     *
     * @param dateTime 时间，单位：毫秒
     * @return 时刻
     */
    public static int getMomentFor15Minute(@NotNull final Long dateTime) {
        return (int) ((dateTime - DateTimeUtil.getDayStartTime(dateTime).getTime()) / (15 * 60 * 1000)) + 1;
    }

    /**
     * 获取指定时间在当天所处于的时刻(5分钟为一个时刻)
     * 例如：dateTime：2018-10-22 01:44:00
     * 从0点0分开始计算，0点0分-0点5为第一个时刻，依次类推该时间的时刻数为
     *
     * @param dateTime 时间，单位：毫秒
     * @return 时刻
     */
    public static int getMomentFor5Minute(@NotNull final Long dateTime) {
        return (int) ((dateTime - DateTimeUtil.getDayStartTime(dateTime).getTime()) / (5 * 60 * 1000)) + 1;
    }


    /**
     * 获取指定时间在当天所处于的时刻(1分钟为一个时刻)
     *
     * @param dateTime 时间，单位：毫秒
     * @return 时刻
     */
    public static int getMomentForMinute(@NotNull final Long dateTime) {
        return (int) ((dateTime - DateTimeUtil.getDayStartTime(dateTime).getTime()) / (1 * 60 * 1000)) + 1;
    }

    /**
     * 获取指定时间所在时刻的标记时间(15分钟为一个时刻)<br>
     * 例如：dateTime:2018-10-22 16:44:00 <br>
     * 标记时间为2018-10-22 16:30:00
     *
     * @param dateTime 时间，单位：毫秒
     */
    public static Date getMomentTimeFor15Minute(@NotNull final Long dateTime) {
        int moment = DateTimeUtil.getMomentFor15Minute(dateTime);// 获取指定时间所在时刻数
        long differentTime = moment * 15 * 60 * 1000;// 获取从0分0秒开始到现在的时间间隔，单位：毫秒
        long dayStartTime = DateTimeUtil.getDayStartTime(dateTime).getTime();
        return new Date(dayStartTime + differentTime - 15 * 60 * 1000);
    }

    /**
     * 获取指定时间所在时刻的标记时间(5分钟为一个时刻)<br>
     * 例如：dateTime:2018-10-22 16:44:00 <br>
     * 标记时间为2018-10-22 16:40:00
     *
     * @param dateTime 时间，单位：毫秒
     * @return 指定时间所在时刻的标记时间，单位：毫秒
     */
    public static Date getMomentTimeFor5Minute(@NotNull final Long dateTime) {
        int moment = DateTimeUtil.getMomentFor5Minute(dateTime);// 获取指定时间所在时刻数
        long differentTime = moment * 5 * 60 * 1000;// 获取从0分0秒开始到现在的时间间隔，单位：毫秒
        long dayStartTime = DateTimeUtil.getDayStartTime(dateTime).getTime();
        return new Date(dayStartTime + differentTime - 5 * 60 * 1000);
    }

    /**
     * 获取当前时间（时间粒度分钟）
     *
     * @return 当前时间
     */
    public static Date getCurrentTimeForMinute() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取指定时间所在天的0点0分
     *
     * @param dateTime 时间毫秒
     * @return 0点0分的毫秒
     */
    public static Date getDayStartTime(@NotNull final Long dateTime) {
        Calendar date = Calendar.getInstance();
        date.setTimeInMillis(dateTime);
        date.set(Calendar.HOUR_OF_DAY, 0);
        date.set(Calendar.MINUTE, 0);
        date.set(Calendar.SECOND, 0);
        date.set(Calendar.MILLISECOND, 0);
        return date.getTime();
    }

    /**
     * 今日凌晨 毫秒
     *
     * @return 时间
     */
    public static long getMillisecondsSubDay() {

        Calendar c = Calendar.getInstance();

        return c.getTimeInMillis() - DateUtils.getFragmentInMilliseconds(c, Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定时间所在天的23点59分59秒
     *
     * @param dateTime 时间毫秒
     * @return 23点59分59秒的毫秒
     */
    public static Date getDayLastTime(@NotNull final Long dateTime) {
        Calendar date = Calendar.getInstance();
        date.setTimeInMillis(dateTime);
        date.set(Calendar.HOUR_OF_DAY, 23);
        date.set(Calendar.MINUTE, 59);
        date.set(Calendar.SECOND, 59);
        date.set(Calendar.MILLISECOND, 999);
        return date.getTime();
    }

    /**
     * 获取两个时间间的间隔天数(自然天)
     *
     * @param dateFrom 开始时间
     * @param dateTo   结束时间
     * @return 自然天数
     */
    public static int getDaysBetweenTwoDate(Long dateFrom, Long dateTo) {
        return (int) ((DateTimeUtil.getDayStartTime(dateTo).getTime() - DateTimeUtil.getDayStartTime(dateFrom).getTime()) / (1000 * 60 * 60 * 24L));
    }

    /**
     * 获取两个时间间的间隔间隔时刻数(15分钟一个时刻)
     *
     * @param dateFrom 开始时间
     * @param dateTo   结束时间
     * @return 间隔时刻数
     */
    public static int getMomentsBetweenTwoDate(Long dateFrom, Long dateTo) {
        return (int) ((dateTo - dateFrom) / (15 * 60 * 1000));
    }

    /**
     * 获取两个时间间的间隔间隔时刻数(5分钟一个时刻)
     *
     * @param dateFrom 开始时间
     * @param dateTo   结束时间
     * @return 间隔时刻数
     */
    public static int getMin5BetweenTwoDate(Long dateFrom, Long dateTo) {
        return (int) ((dateTo - dateFrom) / (5 * 60 * 1000));
    }


    /**
     * 获取现在时间
     *
     * @return 返回字符串格式 yyyy-MM-dd HH:mm:ss
     */
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 将长时间格式时间转换为字符串 yyyy-MM-dd HH:mm:ss
     *
     * @param dateDate
     * @return
     */
    public static String dateToStrLong(Date dateDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(dateDate);
        return dateString;
    }

    /**
     * 将时间格式字符串转换为时间 HH:mm
     *
     * @param strDate
     * @return
     */
    public static Date strToDateLongHM(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    /**
     * 判断tunnel日落
     *
     * @param time
     * @param dfLonIn
     * @param dfLatIn
     * @return boolean 返回true，说明时间在日升日落之间，返回false，说明时间在日升之前或者日落之后。
     */
//    public static boolean checkInSunriseAndSunset(Long time, double dfLonIn, double dfLatIn) {
//        Date date = new Date(time);
//
//        String str1 = SunRiseSet.getSunrise(new BigDecimal(dfLonIn), new BigDecimal(dfLatIn), date);
//        String str2 = SunRiseSet.getSunset(new BigDecimal(dfLonIn), new BigDecimal(dfLatIn), date);
//
//        Date dt1 = null;
//        Date dt2 = null;
//        Date dt3 = null;
//        try {
//            dt1 = strToDateLongHM(dateToStrLong(date).substring(11, 16));
//            dt2 = strToDateLongHM(str1);
//            dt3 = strToDateLongHM(str2);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        if (dt1.getTime() > dt2.getTime() && dt1.getTime() < dt3.getTime()) {
//            return true;
//        } else {
//            return false;
//        }
//    }


    /**
     * 获取日升日落
     *
     * @param time
     * @param dfLonIn
     * @param dfLatIn
     * @return boolean 返回true，说明时间在日升日落之间，返回false，说明时间在日升之前或者日落之后。
     */
//    public static List<Date> getSunriseAndSunset(Long time, double dfLonIn, double dfLatIn) {
//        Date date = new Date(time);
//
//        String str1 = SunRiseSet.getSunrise(new BigDecimal(dfLonIn), new BigDecimal(dfLatIn), date);
//        String str2 = SunRiseSet.getSunset(new BigDecimal(dfLonIn), new BigDecimal(dfLatIn), date);
//
//
//        List<Date> dates = new ArrayList<>();
//        dates.add(strToDateLongHM(str1));
//        dates.add(strToDateLongHM(str2));
//
//        return dates;
//    }

    /**
     * 判断时间是不是今天
     *
     * @param date
     * @return 是返回true，不是返回false
     */
    public static boolean isNow(Date date) {
        //当前时间
        Date now = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
        //获取今天的日期
        String nowDay = sf.format(now);
        //对比的时间
        String day = sf.format(date);
        return day.equals(nowDay);
    }

    /**
     * 取最近的整点，0-30取前面的整点，31-59取后面的整点
     *
     * @param timestamp
     * @return 返回整点时间戳
     */
    public static Long nwpAddStartMinutes(Long timestamp) {
        // 创建 Calendar 对象
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);

        // 获取分钟数
        int minutes = calendar.get(Calendar.MINUTE);

        // 如果分钟数大于等于31，向后一个小时取整点
        if (minutes >= 31) {
            calendar.add(Calendar.HOUR_OF_DAY, 1);
            calendar.set(Calendar.MINUTE, 0);
        } else {
            // 如果分钟数在00和30之间，取当前小时的整点
            calendar.set(Calendar.MINUTE, 0);
        }

        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        // 返回毫秒级时间戳
        return calendar.getTimeInMillis();
    }


    /**
     * 根据调用开始的方法返回的时间往后加5个小时
     *
     * @param time
     * @return 返回当前时间加5个小时时间戳
     */
    public static Long nwpAddEndMinutes(Long time) {
        // 将最近整点的时间戳转换为Instant对象
        Instant startInstant = Instant.ofEpochMilli(time);

        // 将以毫秒为单位的时间间隔转换为秒
        long secondsToAdd = 4 * 60 * 60 + 45 * 60; // 4小时45分钟

        // 往后推4小时45分钟
        Instant newInstant = startInstant.plusMillis(secondsToAdd * 1000);

        return newInstant.toEpochMilli();
    }

    /**
     * 根据当前所处不同时间，返回对应的分钟时间戳
     * 46至00传45末尾的env，01至15传00末尾的env，16至30传15末尾的env，31至45传30末尾的env
     *
     * @param timestamp
     * @return 返回当前时间加5个小时时间戳
     */
    public static Long windMinusEndMinutes(Long timestamp) {
        // 创建 Calendar 对象
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);

        // 获取小时和分钟数
        int hours = calendar.get(Calendar.HOUR_OF_DAY);
        int minutes = calendar.get(Calendar.MINUTE);

        // 根据规定逻辑修改分钟数
        if (minutes >= 1 && minutes <= 15) {
            minutes = 0;
        } else if (minutes <= 30) {
            minutes = 15;
        } else if (minutes <= 45) {
            minutes = 30;
        } else {
            minutes = 45;
        }

        // 设置修改后的小时和分钟数
        calendar.set(Calendar.HOUR_OF_DAY, hours);
        calendar.set(Calendar.MINUTE, minutes);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        // 返回毫秒级时间戳
        return calendar.getTimeInMillis();
    }

    /**
     * 根据传入时间往前推两个小时
     *
     * @param timestamp
     * @return 返回env开始时间戳
     */
    public static long windMinusStartMinutes(long timestamp) {
        // 将毫秒级时间戳转换为Instant对象
        Instant instant = Instant.ofEpochMilli(timestamp);

        // 往前推2小时
        Instant newInstant = instant.minusSeconds((1 * 60 + 45) * 60);

        // 将毫秒部分设为0
        LocalDateTime localDateTime = LocalDateTime.ofInstant(newInstant, ZoneOffset.UTC);
        localDateTime = localDateTime.withSecond(0).withNano(0);

        // 转换为Instant对象
        Instant updatedInstant = localDateTime.toInstant(ZoneOffset.UTC);

        return updatedInstant.toEpochMilli();
    }

    /**
     * 传入系统时间，判断当前时刻，调用不同的HistoryRp
     * 举例： 在46至00传45末尾的his（后5分钟最后一个点替换00末尾，并调用55时刻的历史功率值）
     *
     * @param sysTime
     * @return 返回0就用老方法，返回时间戳就用那个时刻的值作为历史功率值
     */
    public static long methodCategory(long sysTime) {
        long time = 0;
        long minuteTime = sysTime / (60 * 1000);
        int currentMinute = (int) (minuteTime % 60);

        long hourTime = sysTime / (3600 * 1000);

        if (currentMinute >= 11 && currentMinute <= 15) {
            time = hourTime * 3600000L + 10 * 60000L;
        } else if (currentMinute >= 26 && currentMinute <= 30) {
            time = hourTime * 3600000L + 25 * 60000L;
        } else if (currentMinute >= 41 && currentMinute <= 45) {
            time = hourTime * 3600000L + 40 * 60000L;
        } else if (currentMinute >= 56 || currentMinute == 0) {
            time = hourTime * 3600000L + 55 * 60000L;
        }

        return time;
    }


    /**
     * 获取指定时间在当天所处于第多少个
     * 例如：dateTime：2018-10-22 01:44:00
     * 从0点0分开始计算，0点0分-0点5为第一个时刻，依次类推该时间的时刻数为
     *
     * @param dateTime 时间，单位：毫秒
     * @param interval
     * @return 时刻
     */
    public static int getMomentForInterval(@NotNull final Long dateTime, Integer interval) {
        return (int) ((dateTime - DateTimeUtil.getDayStartTime(dateTime).getTime()) / (interval)) + 1;
    }

    /**
     *  获取每一天
     * @param startDate
     * @param endDate
     * @return
     */

    public static List<String> getDatesInRange(Date startDate, Date endDate) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        List<String> dateList = new ArrayList<>();
        Calendar startCalendar = Calendar.getInstance();
        Calendar endCalendar = Calendar.getInstance();

        startCalendar.setTime(startDate);
        endCalendar.setTime(endDate);

        endCalendar.add(Calendar.DAY_OF_YEAR, 1);
        endCalendar.set(Calendar.HOUR_OF_DAY, 0);
        endCalendar.set(Calendar.MINUTE, 0);
        endCalendar.set(Calendar.SECOND, 0);
        endCalendar.set(Calendar.MILLISECOND, 0);

        while (!startCalendar.after(endCalendar)) {
            dateList.add(simpleDateFormat.format(startCalendar.getTime()));
            startCalendar.add(Calendar.DAY_OF_YEAR, 1);
        }

        return dateList;
    }

    /**
     *  判断两个时间相差分钟数
     * @param date1
     * @param date2
     * @param size
     * @return
     */
    public static boolean isMinutesDifference(Date date1, Date date2,Long size) {

        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);

        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);

        long millis1 = calendar1.getTimeInMillis();
        long millis2 = calendar2.getTimeInMillis();

        long diffInMillis = Math.abs(millis1 - millis2);

        long fiveMinutesInMillis = size * 60 * 1000;

        // 判断时间差是否等于5分钟
        return diffInMillis == fiveMinutesInMillis;
    }
}
