package com.ruoyi.common.core.utils;

import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

/**
 * The type Date time util.
 *
 * @author chen.cheng
 */
public class DateTimeUtil {

    /**
     * Current date time string.
     *
     * @return the string
     * @author chen.cheng
     */
    public static String currentDateTime() {
        return DateTimeUtil.currentDateTime(DateFormatter.yyyy_MM_dd_HHmmss);
    }

    /**
     * Parse date date.
     *
     * @param dateString the date string
     * @return the date
     * @author chen.cheng
     */
    public static Date parseDate(String dateString) {
        return DateTimeUtil.parseDate(dateString, DateFormatter.yyyy_MM_dd_HHmmss);
    }

    /**
     * Parse date date.
     *
     * @param dateString the date string
     * @param formatRegex the format regex
     * @return the date
     * @author chen.cheng
     */
    public static Date parseDate(String dateString, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegex);
        LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
        Instant instant = dateTime.atZone(ZoneId.of("GMT+08:00")).toInstant();
        return Date.from(instant);
    }

    /**
     * Minus day date.
     *
     * @param dayNum the day num
     * @return the date
     * @author chen.cheng
     */
    public static Date minusDay(Long dayNum) {
        LocalDateTime now = LocalDateTime.now();
        Instant instant = now.atZone(ZoneId.of("GMT+08:00")).toInstant();
        return Date.from(instant);
    }

    /**
     * Parse date string.
     *
     * @param localDateTime the local date time
     * @param formatRegex the format regex
     * @return the string
     * @author chen.cheng
     */
    public static String parseDate(LocalDateTime localDateTime, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegex);
        return localDateTime.format(formatter);
    }

    /**
     * Parse date string.
     *
     * @param localDate the local date
     * @param formatRegex the format regex
     * @return the string
     * @author chen.cheng
     */
    public static String parseDate(LocalDate localDate, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegex);
        return localDate.format(formatter);
    }

    /**
     * Current date time string.
     *
     * @param format the format
     * @return the string
     * @author chen.cheng
     */
    public static String currentDateTime(String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDateTime now = LocalDateTime.now();
        return now.format(formatter);
    }

    /**
     * Parse local date local date time.
     *
     * @param dateString the date string
     * @param formatRegex the format regex
     * @return the local date time
     * @author chen.cheng
     */
    public static LocalDateTime parseLocalDateTime(String dateString, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegex);
        LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
        return dateTime;
    }

    /**
     * Parse local date local date.
     *
     * @param dateString the date string
     * @param formatRegex the format regex
     * @return the local date
     * @author chen.cheng
     */
    public static LocalDate parseLocalDate(String dateString, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegex);
        LocalDate dateTime = LocalDate.parse(dateString, formatter);
        return dateTime;
    }

    /**
     * Parse date string string.
     *
     * @param dateString the date string
     * @param formatRegex the format regex
     * @return the string
     * @author chen.cheng
     */
    public static String parseDateString(String dateString, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormatter.yyyy_MM_dd_HHmmss);
        LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
        return parseDate(dateTime, formatRegex);
    }

    /**
     * Parse date string string.
     *
     * @param dateString the date string
     * @param srcFormatRegex the src format regex
     * @param formatRegex the format regex
     * @return the string
     * @author chen.cheng
     */
    public static String parseDateString(String dateString, String srcFormatRegex, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(srcFormatRegex);
        LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
        return parseDate(dateTime, formatRegex);
    }

    /**
     * Parse short date string string(yyyyMMdd).
     *
     * @param dateString the date string
     * @param srcFormatRegex the src format regex
     * @param formatRegex the format regex
     * @return the string
     * @author chen.cheng
     */
    public static String parseShortDateString(String dateString, String srcFormatRegex, String formatRegex) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(srcFormatRegex);
        LocalDate date = LocalDate.parse(dateString, formatter);
        return parseDate(date, formatRegex);
    }

    /**
     * Days of range list.
     *
     * @param startDate the start date
     * @param endDate the end date DateTimeUtil.daysOfRange("2020-05-06",
     *            "2020-09-02",DateFormatter.yyyy_MM_dd,DateFormatter.yyyyMMdd)
     * @return the list
     * @author chen.cheng
     */
    public static List<String> daysOfRange(String startDate, String endDate) {
        return daysOfRange(startDate, endDate, DateFormatter.yyyy_MM_dd, DateFormatter.yyyy_MM_dd);
    }

    /**
     * Minus month string.
     *
     * @param monthNum the month num
     * @param formatRegx the format regx
     * @return the string
     * @author chen.cheng
     */
    public static String minusMonth(Long monthNum, String formatRegx) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegx);
        LocalDateTime now = LocalDateTime.now();
        now = now.minusMonths(monthNum);
        return now.format(formatter);
    }

    /**
     * Minus month string
     *
     * @param monthNum month num
     * @param formatRegx format regx
     * @return the string
     * @author chen.cheng
     */
    public static String minusMonth(Integer monthNum, String formatRegx) {
        return minusMonth(monthNum.longValue(), formatRegx);
    }

    /**
     * Yesterday string.
     *
     * @param formatRegx the format regx
     * @return the string
     * @author chen.cheng
     */
    public static String yesterday(String formatRegx) {
        return minusDay(1L, formatRegx);
    }

    /**
     * Minus month string.
     *
     * @param monthNum the month num
     * @param formatRegx the format regx
     * @return the string
     * @author chen.cheng
     */
    public static String minusMonth(String date, String formatRegx, Long monthNum) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegx);
        LocalDateTime localDateTime = parseLocalDateTime(date, formatRegx);
        localDateTime = localDateTime.minusMonths(monthNum);
        return localDateTime.format(formatter);
    }

    /**
     * Date minus month string
     *
     * @param date date
     * @param formatRegx format regx
     * @param monthNum month num
     * @return the string
     * @author chen.cheng
     */
    public static String dateMinusMonth(String date, String formatRegx, Long monthNum) {
        return dateMinusMonth(date, DateFormatter.yyyyMMdd, formatRegx, monthNum);
    }

    /**
     * Date minus month string
     *
     * @param date date
     * @param srcFormatRegx src format regx
     * @param formatRegx format regx
     * @param monthNum month num
     * @return the string
     * @author chen.cheng
     */
    public static String dateMinusMonth(String date, String srcFormatRegx, String formatRegx, Long monthNum) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegx);
        LocalDate localDateTime = parseLocalDate(date, srcFormatRegx);
        localDateTime = localDateTime.minusMonths(monthNum);
        return localDateTime.format(formatter);
    }

    /**
     * Minus day string.
     *
     * @param dayNum the day num
     * @param formatRegx the format regx
     * @return the string
     * @author chen.cheng
     */
    public static String minusDay(Long dayNum, String formatRegx) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatRegx);
        LocalDateTime now = LocalDateTime.now();
        now = now.minusDays(dayNum);
        return now.format(formatter);
    }

    /**
     * Days of range list.
     *
     * @param startDate the start date
     * @param endDate the end date
     * @param paramFormat the param format
     * @param resultFormat the result format
     * @return the list
     * @author chen.cheng
     */
    public static List<String> daysOfRange(String startDate, String endDate, String paramFormat, String resultFormat) {
        DateTimeFormatter resultFormatter = DateTimeFormatter.ofPattern(resultFormat);
        DateTimeFormatter paramFormatter = DateTimeFormatter.ofPattern(paramFormat);
        LocalDate dateTimeOfStart = LocalDate.parse(startDate, paramFormatter);
        LocalDate dateTimeOfEnd = LocalDate.parse(endDate, paramFormatter);
        List<String> days = new ArrayList<>();
        while (!dateTimeOfStart.isAfter(dateTimeOfEnd)) {
            days.add(dateTimeOfStart.format(resultFormatter));
            dateTimeOfStart = dateTimeOfStart.plusDays(1L);
        }
        return days;
    }

    public static List<String> monthOfRange(String startDate, String endDate, String paramFormat, String resultFormat) {
        DateTimeFormatter resultFormatter = DateTimeFormatter.ofPattern(resultFormat);
        DateTimeFormatter paramFormatter = DateTimeFormatter.ofPattern(paramFormat);
        LocalDate dateTimeOfStart = LocalDate.parse(startDate, paramFormatter);
        LocalDate dateTimeOfEnd = LocalDate.parse(endDate, paramFormatter);
        List<String> month = new ArrayList<>();
        while (!dateTimeOfStart.isAfter(dateTimeOfEnd)) {
            month.add(dateTimeOfStart.format(resultFormatter));
            dateTimeOfStart = dateTimeOfStart.plusMonths(1L);
        }
        DateTimeFormatter ddFormatter = DateTimeFormatter.ofPattern(DateFormatter.DD);
        int startDd = Integer.parseInt(dateTimeOfStart.format(ddFormatter));
        int endDd = Integer.parseInt(dateTimeOfEnd.format(ddFormatter));
        if (startDd > endDd) {
            month.add(dateTimeOfEnd.format(resultFormatter));
        }
        return month;
    }

    /**
     * Days of range list.
     *
     * @param startDate the start date
     * @param endDate the end date
     * @param paramFormat the param format
     * @return the list
     * @author chen.cheng
     */
    public static List<String> daysOfRange(String startDate, String endDate, String paramFormat) {
        DateTimeFormatter resultFormatter = DateTimeFormatter.ofPattern(paramFormat);
        DateTimeFormatter paramFormatter = DateTimeFormatter.ofPattern(paramFormat);
        LocalDate dateTimeOfStart = LocalDate.parse(startDate, paramFormatter);
        LocalDate dateTimeOfEnd = LocalDate.parse(endDate, paramFormatter);
        List<String> days = new ArrayList<>();
        while (!dateTimeOfStart.isAfter(dateTimeOfEnd)) {
            days.add(dateTimeOfStart.format(resultFormatter));
            dateTimeOfStart = dateTimeOfStart.plusDays(1L);
        }
        return days;
    }

    /**
     * Gets time step of time index.
     *
     * @param tp the tp
     * @param gap the gap (now + gap minutes)
     * @return the time step of time index
     * @author chen.cheng
     */
    public static Integer getTimeStepOfTimeIndex(Integer tp, Integer gap) {
        return getTimeStepIndexOfNow(tp, LocalDateTime.now().plus(gap, ChronoUnit.MINUTES));
    }

    /**
     * Get time step index of now integer.
     *
     * @param tp the tp
     * @param toDateTime the to date time
     * @return the integer
     * @author chen.cheng
     */
    public static int getTimeStepIndexOfNow(Integer tp, LocalDateTime toDateTime) {
        LocalDateTime zero = LocalDateTime.of(toDateTime.getYear(), toDateTime.getMonth(), toDateTime.getDayOfMonth(),
            0, 0, 0);
        Duration duration = Duration.between(zero, toDateTime);
        double minutes = duration.toMinutes();
        return (int) Math.floor(minutes / tp);
    }



    /**
     * Gets time from step index. tp is min the format hh:mm:00
     *
     * @param stepIndex the step index
     * @param tp the tp
     * @return the time from step index
     * @author chen.cheng
     */
    public static String getTimeFromStepIndex(Integer stepIndex, Integer tp) {
        Integer stepIndex2Min = stepIndex * tp;
        String hour = StringUtils.leftPad(String.valueOf(stepIndex2Min / 60), 2, "0");
        String min = StringUtils.leftPad(String.valueOf(stepIndex2Min % 60), 2, "0");
        return String.format("%s:%s:%s", hour, min, "00");
    }

    /**
     * Gets time from step index with out second.
     *
     * @param stepIndex the step index
     * @param tp the tp
     * @return the time from step index with out second
     * @author chen.cheng
     */
    public static String getTimeFromStepIndexWithOutSecond(Integer stepIndex, Integer tp) {
        Integer stepIndex2Min = stepIndex * tp;
        String hour = StringUtils.leftPad(String.valueOf(stepIndex2Min / 60), 2, "0");
        String min = StringUtils.leftPad(String.valueOf(stepIndex2Min % 60), 2, "0");
        return String.format("%s:%s", hour, min);
    }

    /**
     * Gets day of week.
     *
     * @param date the date
     * @param paramFormatter the param formatter
     * @return the day of week
     * @author chen.cheng
     */
    public static Integer getDayOfWeek(String date, String paramFormatter) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(paramFormatter);
        LocalDate l_da1 = LocalDate.parse(date, formatter);
        DayOfWeek dayOfWeek = l_da1.getDayOfWeek();
        return dayOfWeek.getValue();
    }

    /**
     * Gets ali day of week.
     *
     * @param date the date
     * @return the ali day of week
     * @author chen.cheng
     */
    public static Integer getAliDayOfWeek(String date) {
        return getDayOfWeek(date, DateFormatter.yyyyMMdd) - 1;
    }

    /**
     * Cal week of year integer.
     *
     * @return the integer
     * @author chen.cheng
     */
    public static Integer calWeekOfYear() {
        return calWeekOfYear(currentDateTime(DateFormatter.yyyy_MM_dd));
    }

    public static Integer calLastWeekOfYear() {
        return calWeekOfYear(currentDateTime(DateFormatter.yyyy_MM_dd)) - 1;
    }

    /**
     * Cal week of year integer.
     *
     * @param date the date yyyy-MM-dd
     * @return the integer
     * @author chen.cheng
     */
    public static Integer calWeekOfYear(String date) {
        LocalDate localDate = LocalDate.parse(date);
        // 第一个参数：一周的第一天，不能为空
        // 第二个参数：第一周的最小天数，从1到7
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        return localDate.get(weekFields.weekOfYear());
    }

    /**
     * Cal week of year integer.
     *
     * @param date the date
     * @param paramFormatter the param formatter
     * @return the integer
     * @author chen.cheng
     */
    public static Integer calWeekOfYear(String date, String paramFormatter) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(paramFormatter);
        LocalDate localDate = LocalDate.parse(date, formatter);

        return calWeekOfYear(localDate.toString());
    }

    /**
     * Get week time section string [ ].
     *
     * @param weekIndex the week index
     * @return the string [ ]
     * @author chen.cheng
     */
    public static String[] getWeekTimeSection(int weekIndex) {
        return getWeekTimeSection(weekIndex, DateFormatter.yyyyMMdd);
    }

    /**
     * Get week time section string [ ]. 获取前几周内的日期范围
     *
     * @param weekIndex the week index
     * @return the string [ ]
     * @author chen.cheng
     */
    public static String[] getWeekTimeSection(int weekIndex, String dateFormat) {
        // weekIndex为前几周的周数，如：获取当前时间前第二周的时间 weekIndex=2
        // 获取本周第一天
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.WEEK_OF_MONTH, 0);
        cal.set(Calendar.DAY_OF_WEEK, 2);
        Date first = cal.getTime();
        Date last = cal.getTime();
        String firstString = new SimpleDateFormat("yyyy-MM-dd").format(first) + " 00:00:00";
        String lastString = new SimpleDateFormat("yyyy-MM-dd").format(last) + " 23:59:59";
        DateTimeFormatter formatPatten = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime firstTime = LocalDateTime.parse(firstString, formatPatten);
        LocalDateTime lastTime = LocalDateTime.parse(lastString, formatPatten);
        DateTimeFormatter resultDateFormat = DateTimeFormatter.ofPattern(dateFormat);
        // 开始时间
        firstTime = firstTime.plusDays(-(weekIndex * 7L));
        // 结束时间
        lastTime = lastTime.plusDays(-1);
        return new String[] {
            firstTime.format(resultDateFormat), lastTime.format(resultDateFormat)
        };
    }

    public static String getFirstDayOfRecentYear(String dateFormat) {
        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 计算近一年的年份
        int recentYear = today.getYear() - 1;

        LocalDate localDate = LocalDate.of(recentYear, today.getMonth(), 1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        return localDate.format(formatter);
    }

    public static String getFirstDayOfThisYear(String dateFormat) {
        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 计算近一年的年份
        int recentYear = today.getYear();

        // 使用 TemporalAdjusters 来找到近一年的第一天
        LocalDate localDate = LocalDate.of(recentYear, 1, 1).with(TemporalAdjusters.firstDayOfYear());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        return localDate.format(formatter);
    }

    public static String getFirstDayOfRecentYear() {
        return getFirstDayOfRecentYear(DateFormatter.yyyy_MM_dd);
    }

    public static String getFirstDayOfRecentMonth(String dateFormat) {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        // 使用 TemporalAdjusters 来找到近一个月的第一天
        LocalDate with = today.minusMonths(1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        return with.format(formatter);
    }

    public static String getFirstDayOfRecentMonth() {
        return getFirstDayOfRecentMonth(DateFormatter.yyyy_MM_dd);
    }

    public static void main(String[] args) {

    }

    /**
     * The interface Date formatter.
     *
     * @author chen.cheng
     */
    public interface DateFormatter {
        /**
         * The constant yyyy_MM_dd_HHmmss.
         *
         * @author chen.cheng
         */
        String yyyy_MM_dd_HHmmss = "yyyy-MM-dd HH:mm:ss";

        /**
         * The constant yyyyMMddHHmmss.
         *
         * @author chen.cheng
         */
        String yyyyMMddHHmmss = "yyyyMMddHHmmss";

        /**
         * The constant yyyy_MM_dd.
         *
         * @author chen.cheng
         */
        String yyyy_MM_dd = "yyyy-MM-dd";

        String DD = "dd";

        /**
         * The constant yyyyMMdd.
         *
         * @author chen.cheng
         */
        String yyyyMMdd = "yyyyMMdd";

        String yyyy_MM = "yyyy-MM";

        /**
         * The constant yyyyMM.
         *
         * @author chen.cheng
         */
        String yyyyMM = "yyyyMM";

        /**
         * The constant yyyy.
         *
         * @author chen.cheng
         */
        String yyyy = "yyyy";

        /**
         * To string string.
         *
         * @return the string
         * @author chen.cheng
         */
        @Override
        String toString();
    }

}
