package com.sonic.frog.utils;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.time.zone.ZoneRules;
import java.util.*;

/**
 * code
 */
@Slf4j
public class DateConvertUtils {


    /**
     * 获取本周周一的日期
     * @return
     */
    public static String getMondayOfThisWeek() {
        LocalDate monday = LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)).plusDays(1);
        return monday.toString();
    }

    /**
     * 根据年和周数获取周的所有日期时间
     * @param year
     * @param num
     * @param dayOfWeekList
     * @return
     */
    public static List<LocalDate> getDateByYearAndWeekNumAndDayOfWeek(Integer year, Integer num, List<DayOfWeek> dayOfWeekList) {
        List<LocalDate> result = Lists.newArrayList();
        if(CollectionUtils.isEmpty(dayOfWeekList)) {
            dayOfWeekList = Lists.newArrayList(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
        }
        //周数小于10在前面补个0
        String numStr = num < 10 ? "0" + String.valueOf(num) : String.valueOf(num);
        //2019-W01-01获取第一周的周一日期，2019-W02-07获取第二周的周日日期
        for (DayOfWeek dayOfWeek : dayOfWeekList) {
            String weekDate = String.format("%s-W%s-%s", year, numStr, dayOfWeek.getValue());
            LocalDate localDate = LocalDate.parse(weekDate, DateTimeFormatter.ISO_WEEK_DATE);
            result.add(localDate);
        }
        return result;
    }

    /**
     * 获取当前年中的周
     * @return
     */
    public static int getYearWeek() {
        LocalDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toLocalDateTime();
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,4);
        int week = now.get(weekFields.weekOfWeekBasedYear());
        return week;
    }

    public static String format(LocalDateTime localDateTime, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime.minusHours(15));
        return date;
    }

    public static String format(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime.minusHours(15));
        return date;
    }

    /**
     * 将符合 "yyyy-MM-dd" 格式的日期字符串转换为 LocalDateTime 对象。
     *
     * @param dateString 日期字符串
     * @return LocalDateTime 对象
     */
    public static LocalDateTime convertStringToDateTime(String dateString) {
        // 定义DateTimeFormatter，指定日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        // 将字符串解析为LocalDate
        LocalDate date = LocalDate.parse(dateString, formatter);
        // 将LocalDate转换为LocalDateTime，时间部分设为00:00
        LocalDateTime dateTime = date.atStartOfDay();
        return dateTime;
    }

    /**
     * 将符合 "yyyy-MM-dd" 格式的日期字符串转换为 LocalDateTime 对象。
     *
     * @param dateString 日期字符串
     * @return LocalDateTime 对象
     */
    public static LocalDateTime convertStringToDateTimeV2(String dateString) {
        // 定义DateTimeFormatter，指定日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 将字符串解析为LocalDate
        LocalDate date = LocalDate.parse(dateString, formatter);
        // 将LocalDate转换为LocalDateTime，时间部分设为00:00
        LocalDateTime dateTime = date.atStartOfDay();
        return dateTime;
    }

    /**
     * 计算两个 LocalDateTime 对象之间的天数差。
     *
     * @param startDate 起始日期时间
     * @param endDate   结束日期时间
     * @return 两个日期相差的天数，如果 startDate 在 endDate 之后，返回负数
     */
    public static long calculateDaysBetween(LocalDateTime startDate, LocalDateTime endDate) {
        return ChronoUnit.DAYS.between(startDate, endDate);
    }

    /**
     * 计算两个时间相差的秒数
     * @param startDate
     * @param endDate
     * @return
     */
    public static long calculateDaysBetweenSeconds(LocalDateTime startDate, LocalDateTime endDate) {
        return ChronoUnit.SECONDS.between(startDate, endDate);
    }

    public static String noTimeZoneChangeFormat(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String date = dateTimeFormatter.format(localDateTime);
        return date;
    }

    public static String noTimeZoneChangeFormat(LocalDate localDate) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String date = localDate.format(dateTimeFormatter);
        return date;
    }

    public static String format() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toLocalDateTime());
        return date;
    }

    public static String format(Date date) {
        try {
            SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd");
            String result = dateTimeFormatter.format(date);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String formatHms(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime);
        return date;
    }

    public static String formatYearMonthDay(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime);
        return date;
    }

    public static String formatYearMonth(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime);
        return date;
    }

    public static Integer formatYearMonthInt(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM");
        //美国洛杉矶时间
        String date = dateTimeFormatter.format(localDateTime);
        return Integer.valueOf(date);
    }

    public static LocalDateTime getDateMinTime(){
        LocalDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toLocalDateTime()
                .with(LocalTime.MIN);
        return now;
    }

    public static LocalDateTime getDateMinTime(LocalDateTime localDateTime){
        LocalDateTime now = localDateTime.now(ZoneId.of("America/Los_Angeles"))
                .with(LocalTime.MIN);
        return now;
    }

    public static LocalDateTime getDateMaxTime(){
        LocalDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toLocalDateTime()
                .with(LocalTime.MAX);
        return now;
    }

    public static LocalDateTime getDateMaxTime(LocalDateTime localDateTime){
        LocalDateTime now = localDateTime.now(ZoneId.of("America/Los_Angeles"))
                .with(LocalTime.MAX);
        return now;
    }

    public static LocalDateTime getDateLaTime(){
        LocalDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toLocalDateTime();
        return now;
    }


    /**
     * 这里有个坑，传入的时间不是指参数转成La时间而是.now....
     * @param localDateTime
     * @return
     */
    @Deprecated
    public static LocalDateTime getDateLaTime(LocalDateTime localDateTime){
        LocalDateTime now = localDateTime.now(ZoneId.of("America/Los_Angeles"));
        return now;
    }

    public static Integer formatToLocalInt(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String date = dateTimeFormatter.format(localDateTime);
        return Integer.valueOf(date);
    }

    /**
     * 获取洛杉矶 夏令时、冬令时 和北京时间的小时差
     * @return
     */
    public static int getLaHours() {
        Duration duration = Duration.between(getDateLaTime(), LocalDateTime.now());
        Long hours = duration.toHours();//相差的小时数
        return hours.intValue();
    }

    /**
     * 传入指定时间和时区
     * @param localDateTime
     * @param zoneId
     * @return true 表示是夏令时，false表示是冬令时
     */
    public static boolean isDaylightTime(LocalDateTime localDateTime, ZoneId zoneId) {
        ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
        ZoneRules rules = zoneId.getRules();
        boolean flag = rules.isDaylightSavings(zonedDateTime.toInstant());
        return flag;
    }

    /**
     * 获取两个时间相差的秒数
     * @param startTime
     * @param endTime
     * @return
     */
    public static Long getTimeDifference(LocalDateTime startTime, LocalDateTime endTime) {
        Duration duration = Duration.between(startTime, endTime);
        return duration.getSeconds();
    }

    /**
     * 获得某个月最大天数
     * @param year 年份
     * @param month 月份 (1-12)
     * @return 某个月最大天数
     */
    public static int getMaxDayByYearMonth(int year, int month) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        return calendar.getActualMaximum(Calendar.DATE);
    }


    /**
     * 获取最大连续天数
     * @param dayList
     * @return
     */
    public static Integer maxConsecutiveDays(TreeSet<Integer> dayList) {
        try {
            List<Integer> list = new ArrayList<>();
            for (Integer day : dayList) {
                list.add(Integer.valueOf(day.toString().substring(6, 8)));
            }
            int year = LocalDateTime.now().minusHours(15).getYear();
            int month = LocalDateTime.now().minusHours(15).getMonthValue() - 1;
            if(month < 0) {
                year = year - 1;
                month = 12;
            }
            int maxDay = DateConvertUtils.getMaxDayByYearMonth(year, month);
            System.out.println(maxDay);
            int day = 0;
            int index = 0;
            for (int i = 0; i < list.size(); i++) {
                if(day > list.get(i)) {
                    index = i;
                    break;
                }
                day = list.get(i);
            }
            List<Integer> lastMonthList = list.subList(0, index);
            List<Integer> monthList = list.subList(index, list.size());
            TreeSet<Integer> treeSet = new TreeSet(monthList);
            if(CollectionUtils.isNotEmpty(lastMonthList)) {
                for (Integer lastMonthDay : lastMonthList) {
                    treeSet.add(lastMonthDay - maxDay);
                }
            }
            Map<Integer, Integer> groupDateCountMap = new HashMap<>();
            int i = 0;
            for (Integer value : treeSet) {
                value = value - i;
                Integer count = groupDateCountMap.get(value);
                count = count == null ? 1 : count + 1;
                groupDateCountMap.put(value, count);
                i++;
            }
            if(groupDateCountMap.keySet().size() > 0) {
                //获取最大的连续登录天数
                Integer cond = groupDateCountMap.values().stream().max(Comparator.comparing(e -> e)).get();
                return cond == null ? 0 : cond;
            }
            return dayList.size();
        } catch (NumberFormatException e) {
            return dayList.size();
        }
    }

    /**
     * 获取指定年、周数的日期 (参考：https://blog.csdn.net/weixin_44919928/article/details/100008249)
     * @param year
     * @param num
     * @param dayOfWeek(MONDAY TUESDAY)
     * @return
     */
    public static LocalDate getDateByYearAndWeekNumAndDayOfWeekParam(Integer year, Integer num, DayOfWeek dayOfWeek) {
        //周数小于10在前面补个0
        String numStr = num < 10 ? "0" + String.valueOf(num) : String.valueOf(num);
        //2019-W01-01获取第一周的周一日期，2019-W02-07获取第二周的周日日期
        String weekDate = String.format("%s-W%s-%s", year, numStr, dayOfWeek.getValue());
        return LocalDate.parse(weekDate, DateTimeFormatter.ISO_WEEK_DATE);
    }

    /**
     * 判断开始的年月是否大于结束的年月
     * @param startYearMonth
     * @param endYearMonth
     * @return
     */
    public static Boolean yearMonthCheck(String startYearMonth, String endYearMonth) {
        int startYearMonthInt = Integer.valueOf(startYearMonth.replace("-", ""));
        int endYearMonthInt = Integer.valueOf(endYearMonth.replace("-", ""));
        return startYearMonthInt >  endYearMonthInt;
    }


    /**
     * 检查今天是否在起止时间段内
     * @param currentDay
     * @param startDay
     * @param endDay
     * @return
     */
    public static Boolean currentDayInRangeDayCheck(String currentDay, String startDay, String endDay) {
        int startDayInt = Integer.valueOf(startDay.replace("-", ""));
        int endDayInt = Integer.valueOf(endDay.replace("-", ""));
        int currentDayInt = Integer.valueOf(currentDay.replace("-", ""));
        return currentDayInt >= startDayInt && currentDayInt <= endDayInt;
    }

    /**
     * 获取指定时间段内的日志列表
     * @param start
     * @param end
     * @return
     */
    public static List<String> getDateStringsBetween(String start, String end) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(start, formatter);
        LocalDate endDate = LocalDate.parse(end, formatter);

        List<String> dateStrings = new ArrayList<>();
        LocalDate temp = startDate;

        while (!temp.isAfter(endDate)) {
            dateStrings.add(temp.format(formatter));
            temp = temp.plus(1, ChronoUnit.DAYS);
        }

        return dateStrings;
    }

    public static LocalDateTime convertTimeZonePstToCtt(LocalDateTime dateTime) {
        ZoneId fromZone = ZoneId.of("America/Los_Angeles");
        ZoneId toZone = ZoneId.of("Asia/Shanghai");
        // 将LocalDateTime转换为ZonedDateTime，使用源时区
        ZonedDateTime fromZonedDateTime = ZonedDateTime.of(dateTime, fromZone);

        // 将ZonedDateTime转换为目标时区的ZonedDateTime
        ZonedDateTime toZonedDateTime = fromZonedDateTime.withZoneSameInstant(toZone);

        // 返回目标时区的LocalDateTime
        return toZonedDateTime.toLocalDateTime();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        System.out.println("第" + 1 + "周，周一日期:" + getDateByYearAndWeekNumAndDayOfWeekParam(2021, 1, DayOfWeek.MONDAY).toString());
        System.out.println("第" + 1 + "周，周一日期:" + getDateByYearAndWeekNumAndDayOfWeekParam(2021, 1, DayOfWeek.SUNDAY).toString());
    }

}
