package com.ruoyi.common.utils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.apache.commons.lang3.time.DateFormatUtils;

/**
 * 时间工具类
 *
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    public static String YYYY = "yyyy";

    public static String YYYY_MM = "yyyy-MM";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static String YYYYMMDD = "yyyyMMdd";

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM","HH:mm:ss",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");

    public static String getDateNow() {
        return dateTimeNow(YYYYMMDD);
    }

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static String getYear() {
        return dateTimeNow(YYYY);
    }

    public static final String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static final String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static final String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static final String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static final String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static final Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static final String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将字符串转换为Date对象（支持多种格式）
     * @param dateString 日期字符串
     * @return 对应的Date对象，解析失败返回null
     */
    public static Date convertStringToDate(String dateString) {
        // 定义可能的日期格式（按优先级排序）

        for (String pattern : parsePatterns) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
                LocalDate localDate = LocalDate.parse(dateString, formatter);
                return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
            } catch (DateTimeParseException e) {
                // 尝试下一个格式
            }
        }
        return null; // 所有格式都尝试失败
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 计算相差天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }

    /**
     * 计算两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }

    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }

    /**
     * 根据时间加减时间（时间可为负）
     *
     * @param dateTime 时间
     * @param seconds  加减的天数
     * @param format   日期格式（与dateTime格式一致）
     * @return
     */
    public static String plusSeconds(String dateTime, int seconds, String format) {
        //LocalDateTime parse = parse(dateTime, format);
        //LocalDateTime localDateTime = parse.plusMinutes(seconds);
        LocalDate parse = LocalDate.parse(dateTime);
        LocalDate localDate = LocalDate.now().plusDays(seconds);
        String date = localDate.format(DateTimeFormatter.ofPattern(format));
        return date;
    }


    /***
     * 判断某一个日期是星期几
     * @param datetime
     * @return
     */
    public static String dateToWeek(String datetime) {

        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        String[] weekDays = {"0", "1", "2", "3",
                "4", "5", "6"};
        Calendar cal = Calendar.getInstance(); // 获得一个日历
        Date datet = null;
        try {
            datet = f.parse(datetime);
            cal.setTime(datet);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1; // 指示一个星期中的某天。
        if (w < 0)
            w = 0;
        return weekDays[w];

    }

    /***
     * 获取传入日期的前一天日期
     * @param dateTime
     * @return
     */
    public static String getYesterday(String dateTime) {
        SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD);
        Date date = null;
        try {
            date = sdf.parse(dateTime);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        // 往前一天
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, -1);
        Date dateMinusOneDay = calendar.getTime();
        String dateMinusOneDayStr = sdf.format(dateMinusOneDay);
        System.out.println("往前一天的日期: " + dateMinusOneDayStr);
        return dateMinusOneDayStr;
    }

    /**
     * 获取指定月份的最后一天日期（格式为yyyy-MM-dd）
     *
     * @param year 年份（如2023）
     * @param month 月份（1-12）
     * @return 最后一天日期字符串（如"2023-02-28"）
     * @throws IllegalArgumentException 如果月份不在1-12范围内
     */
    public static String getLastDayOfMonth(int year, int month) {
        // 验证月份是否有效
        if (month < 1 || month > 12) {
            throw new IllegalArgumentException("月份必须在1-12之间");
        }

        // 使用YearMonth获取月份的最后一天并格式化为字符串
        return YearMonth.of(year, month)
                .atEndOfMonth()
                .format(DateTimeFormatter.ISO_DATE);
    }


    /**
     * 判断目标日期是否在[startDate, endDate]区间内（包含边界）
     *
     * @param target   目标日期 (Date类型)
     * @param start    开始日期 (Date类型)
     * @param end      结束日期 (Date类型)
     * @return 若target在[start, end]区间内返回true，否则返回false
     */
    public static boolean isDateBetween(Date target, Date start, Date end) {
        // 转换为LocalDate（忽略时间部分）
        LocalDate targetDate = convertToLocalDate(target);
        LocalDate startDate = convertToLocalDate(start);
        LocalDate endDate = convertToLocalDate(end);

        // 判断日期区间（包含边界）
        return (targetDate.isEqual(startDate) || targetDate.isAfter(startDate))
                && (targetDate.isEqual(endDate) || targetDate.isBefore(endDate));
    }

    /**
     * 将Date转换为LocalDate（格式化为yyyy-MM-dd）
     */
    private static LocalDate convertToLocalDate(Date date) {
        return date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
    }

    /**
     *  获取当期月份，只要月份，不要年
     */
    public static String justMonth() {
        // 获取当前月份数字
        int monthNumber = LocalDate.now().getMonthValue();
        // 获取带前导零的月份字符串
        String monthPadded = String.format("%02d", monthNumber);
        return monthPadded;
    }

    /**
     * 获取本周周一至周六的日期字符串列表（格式：yyyy-MM-dd）
     * @return 包含6个日期字符串的List
     */
    public static List<String> getWeekDatesFromMondayToSaturday() {
        List<String> dates = new ArrayList<>(6);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // 获取本周周一（如果今天是周一则返回今天）
        LocalDate monday = LocalDate.now()
                .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));

        // 添加周一到周六的日期字符串
        for (int i = 0; i < 6; i++) {
            dates.add(monday.plusDays(i).format(formatter));
        }

        return dates;
    }

    /**
     * 获取月份所有日期（默认格式 yyyy-MM-dd）
     */
    public static List<String> getAllDateStringsInMonth(int year, int month) {
        return getAllDateStringsInMonth(year, month, "yyyy-MM-dd");
    }

    /**
     * 获取月份所有日期（自定义格式）
     * @param year    年份
     * @param month   月份 (1-12)
     * @param pattern 日期格式模式 (如 "yyyy-MM-dd", "MM/dd/yyyy")
     * @return 日期字符串列表
     */
    public static List<String> getAllDateStringsInMonth(int year, int month, String pattern) {
        YearMonth yearMonth = YearMonth.of(year, month);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        return IntStream.rangeClosed(1, yearMonth.lengthOfMonth())
                .mapToObj(day -> LocalDate.of(year, month, day))
                .map(date -> date.format(formatter))
                .collect(Collectors.toList());
    }

    /**
     * 比较两个时间字符串
     * @param time1 时间字符串1
     * @param time2 时间字符串2
     * @param pattern 时间格式（如 "yyyy-MM-dd HH:mm:ss"）
     * @return
     *   -1：time1 < time2
     *    0：time1 = time2
     *    1：time1 > time2
     * @throws DateTimeParseException 当格式不匹配时抛出异常
     */
    public static int compareTimes(String time1, String time2, String pattern) {
        Objects.requireNonNull(time1, "Time1 cannot be null");
        Objects.requireNonNull(time2, "Time2 cannot be null");

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        // 根据格式长度自动判断时间类型
        if (pattern.contains("y") && pattern.length() <= 10) {
            LocalDate date1 = LocalDate.parse(time1, formatter);
            LocalDate date2 = LocalDate.parse(time2, formatter);
            return date1.compareTo(date2);
        } else if (!pattern.contains("y")) {
            LocalTime t1 = LocalTime.parse(time1, formatter);
            LocalTime t2 = LocalTime.parse(time2, formatter);
            return t1.compareTo(t2);
        } else {
            LocalDateTime dt1 = LocalDateTime.parse(time1, formatter);
            LocalDateTime dt2 = LocalDateTime.parse(time2, formatter);
            return dt1.compareTo(dt2);
        }
    }

    /**
     * 检查目标时间是否在时间区间内（包含边界）
     *
     * @param target 目标时间 (格式: HH:mm:ss)
     * @param start 开始时间 (格式: HH:mm:ss)
     * @param end 结束时间 (格式: HH:mm:ss)
     * @return
     *   true - 在区间内（包含边界）
     *   false - 不在区间内
     * @throws DateTimeParseException 如果时间格式无效
     */
    public static boolean isInTimeRange(String target, String start, String end) {
        LocalTime targetTime = parseTime(target);
        LocalTime startTime = parseTime(start);
        LocalTime endTime = parseTime(end);

        // 处理跨午夜的时间区间（如 23:00 到 01:00）
        if (startTime.isAfter(endTime)) {
            // 目标时间在开始时间之后（如 23:30）或在结束时间之前（如 00:30）
            return targetTime.isAfter(startTime) || targetTime.equals(startTime) ||
                    targetTime.isBefore(endTime) || targetTime.equals(endTime);
        } else {
            // 普通时间区间（不跨午夜）
            return (targetTime.isAfter(startTime) || targetTime.equals(startTime)) &&
                    (targetTime.isBefore(endTime) || targetTime.equals(endTime));
        }
    }

    // 解析时间字符串
    private static LocalTime parseTime(String timeStr) {
        return LocalTime.parse(timeStr, TIME_FORMATTER);
    }


    /**
     * 获取两个日期之间的所有日期（包含开始和结束日期），返回字符串列表
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 日期字符串列表（格式：yyyy-MM-dd）
     */
    public static List<String> getDateStringsBetween(Date startDate, Date endDate) {
        List<String> dateStrings = new ArrayList<>();

        if (startDate == null || endDate == null) {
            return dateStrings;
        }

        // 转换为Java 8的LocalDate
        LocalDate start = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        if (start.isAfter(end)) {
            return dateStrings;
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        while (!start.isAfter(end)) {
            dateStrings.add(formatter.format(start));
            start = start.plusDays(1);
        }

        return dateStrings;
    }

    public static void main(String[] args) throws ParseException {

        try {
            // 1. 准备日期部分
            LocalDate datePart = LocalDate.of(2023, 12, 25);

            // 2. 准备时间部分（只关心时间）
            // 创建时间对象（日期部分无关紧要）
            Date timePart = parseDate("17:44:52"); // 当前时间
            System.out.println("原始时间Date: " + timePart);

            // 3. 组合日期和时间
            LocalDateTime result = combineDateTime(datePart, timePart);

            // 4. 输出结果
            System.out.println("组合后的LocalDateTime: " + result);
            System.out.println("格式化输出: " +
                    result.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

            // 5. 测试边界情况
            // 午夜时间
            Date midnight = java.util.Date.from(
                    java.time.LocalTime.MIDNIGHT.atDate(LocalDate.now())
                            .atZone(ZoneId.systemDefault())
                            .toInstant()
            );
            LocalDateTime midnightResult = combineDateTime(datePart, midnight);
            System.out.println("午夜时间组合: " + midnightResult);

            // 最大时间
            Date maxTime = java.util.Date.from(
                    java.time.LocalTime.MAX.atDate(LocalDate.now())
                            .atZone(ZoneId.systemDefault())
                            .toInstant()
            );
            LocalDateTime maxResult = combineDateTime(datePart, maxTime);
            System.out.println("最大时间组合: " + maxResult);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 转换日期类型
    public static LocalDateTime convertToLocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 获取两个日期之间的所有日期（包含开始和结束日期）
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 日期列表
     */
    public static List<LocalDate> getDateRange(LocalDate startDate, LocalDate endDate) {
        List<LocalDate> dates = new ArrayList<>();

        // 计算总天数（包含结束日）
        long numOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;

        // 通过循环添加每一天
        for (long i = 0; i < numOfDays; i++) {
            dates.add(startDate.plusDays(i));
        }

        return dates;
    }

    /**
     * 将 LocalDate 转换为字符串
     * @param date 要转换的日期
     * @param pattern 日期格式模式（可选），如果为null则使用默认格式
     * @return 格式化后的日期字符串
     */
    public static String convertToString(LocalDate date, String pattern) {
        if (date == null) {
            return null;
        }

        if (pattern != null && !pattern.isEmpty()) {
            // 使用自定义格式
            return DateTimeFormatter.ofPattern(pattern).format(date);
        } else {
            // 使用默认格式 (ISO_LOCAL_DATE)
            return date.toString();
        }
    }

    // 获取星期数
    public static String getWeekday(LocalDate date) {
        int weekValue = date.getDayOfWeek().getValue();
        return weekValue == 7 ? "0" : String.valueOf(weekValue); // 周日特殊处理
    }

    /**
     * 将时间Date对象与LocalDate组合成LocalDateTime
     *
     * @param dateOnly 日期部分（LocalDate）
     * @param timeOnly 时间部分（Date对象，只取时间部分）
     * @return 组合后的LocalDateTime对象
     * @throws IllegalArgumentException 如果任一参数为null
     */
    public static LocalDateTime combineDateTime(LocalDate dateOnly, Date timeOnly) {
        // 验证输入
        if (dateOnly == null) {
            throw new IllegalArgumentException("日期部分不能为null");
        }
        if (timeOnly == null) {
            throw new IllegalArgumentException("时间部分不能为null");
        }

        // 提取时间部分（忽略日期部分）
        LocalTime localTime = timeOnly.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalTime();

        // 组合日期和时间
        return LocalDateTime.of(dateOnly, localTime);
    }


}
