package cn.iocoder.yudao.framework.common.util.date;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.text.SimpleDateFormat;
import java.time.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 时间工具类
 */
public class DateUtils {

    /**
     * 时区 - 默认
     */
    public static final String TIME_ZONE_DEFAULT = "GMT+8";

    /**
     * 秒转换成毫秒
     */
    public static final long SECOND_MILLIS = 1000;

    public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";

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

    public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";

    public static final String IS0_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

    /**
     * 根据传入的时间格式，将Date对象，转换成对应的时间格式
     *
     * @param date
     * @param format
     * @return
     */
    public static String dateFormat(Date date, String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        String formattedDate = formatter.format(date);
        return formattedDate;
    }

    /**
     * 将 LocalDateTime 转换成 Date
     *
     * @param date LocalDateTime
     * @return LocalDateTime
     */
    public static Date of(LocalDateTime date) {
        if (date == null) {
            return null;
        }
        // 将此日期时间与时区相结合以创建 ZonedDateTime
        ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
        // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
        Instant instant = zonedDateTime.toInstant();
        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
        return Date.from(instant);
    }

    /**
     * 将 Date 转换成 LocalDateTime
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime of(Date date) {
        if (date == null) {
            return null;
        }
        // 转为时间戳
        Instant instant = date.toInstant();
        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    public static Date addTime(Duration duration) {
        return new Date(System.currentTimeMillis() + duration.toMillis());
    }

    public static boolean isExpired(Date time) {
        return System.currentTimeMillis() > time.getTime();
    }

    public static boolean isExpired(LocalDateTime time) {
        LocalDateTime now = LocalDateTime.now();
        return now.isAfter(time);
    }

    public static long diff(Date endTime, Date startTime) {
        return endTime.getTime() - startTime.getTime();
    }

    /**
     * 创建指定时间
     *
     * @param year  年
     * @param mouth 月
     * @param day   日
     * @return 指定时间
     */
    public static Date buildTime(int year, int mouth, int day) {
        return buildTime(year, mouth, day, 0, 0, 0);
    }

    /**
     * 创建指定时间
     *
     * @param year   年
     * @param mouth  月
     * @param day    日
     * @param hour   小时
     * @param minute 分钟
     * @param second 秒
     * @return 指定时间
     */
    public static Date buildTime(int year, int mouth, int day,
                                 int hour, int minute, int second) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, mouth - 1);
        calendar.set(Calendar.DAY_OF_MONTH, day);
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, second);
        calendar.set(Calendar.MILLISECOND, 0); // 一般情况下，都是 0 毫秒
        return calendar.getTime();
    }

    /**
     * 创建指定时间
     *
     * @param timeStr 时间字符串，格式为 HH:mm
     * @return 指定时间
     */
    public static Date buildHHmmTime(String timeStr) {
        String[] time = timeStr.split(":");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(time[0]));
        calendar.set(Calendar.MINUTE, Integer.parseInt(time[1]));
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date endTime = calendar.getTime();
        return calendar.getTime();
    }

    public static Date buildWeekHHmmTime(String timeStr) {
        String[] time = timeStr.split(",");
        int week = Integer.parseInt(time[0]);
        boolean isItTheNextDay = false;
        if (week == 7) {
            isItTheNextDay = true;
        }
        week = (week + 1) == 8 ? 7 : (week + 1);
        String[] hhmm = time[1].split(":");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_WEEK, week);
        calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hhmm[0]));
        calendar.set(Calendar.MINUTE, Integer.parseInt(hhmm[1]));
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return isItTheNextDay ? DateUtil.offsetDay(calendar.getTime(), 1) : calendar.getTime();
    }


    public static Date max(Date a, Date b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.compareTo(b) > 0 ? a : b;
    }

    public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.isAfter(b) ? a : b;
    }

    /**
     * 计算当期时间相差的日期
     *
     * @param field  日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
     * @param amount 相差的数值
     * @return 计算后的日志
     */
    public static Date addDate(int field, int amount) {
        return addDate(null, field, amount);
    }

    /**
     * 计算当期时间相差的日期
     *
     * @param date   设置时间
     * @param field  日历字段 例如说，{@link Calendar#DAY_OF_MONTH} 等
     * @param amount 相差的数值
     * @return 计算后的日志
     */
    public static Date addDate(Date date, int field, int amount) {
        if (amount == 0) {
            return date;
        }
        Calendar c = Calendar.getInstance();
        if (date != null) {
            c.setTime(date);
        }
        c.add(field, amount);
        return c.getTime();
    }

    /**
     * 是否今天
     *
     * @param date 日期
     * @return 是否
     */
    public static boolean isToday(LocalDateTime date) {
        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
    }

    /**
     * 获取两个时间区间 - 获取两个时间区间的所有日期
     *
     * @param beginTime
     * @param endTime
     * @return
     */
    public static List<String> betweenDayStrList(String beginTime, String endTime) {
        List<String> list = new ArrayList<>();
        Date begin = DateUtil.parse(beginTime, "yyyy-MM-dd").toJdkDate();
        Date end = DateUtil.parse(endTime, "yyyy-MM-dd").toJdkDate();
        int num = (int) DateUtil.betweenDay(begin, end, true);
        for (int i = 0; i <= num; i++) {
            Date time = DateUtil.offsetDay(begin, i).toJdkDate();
            list.add(DateUtil.format(time, "yyyy-MM-dd"));
        }
        return list;
    }

    /**
     * 获取两个时间区间 - 获取两个时间区间的所有日期
     *
     * @param beginTime
     * @param endTime
     * @return
     */
    public static List<String> betweenDayList(Date beginTime, Date endTime) {
        List<String> list = new ArrayList<>();
        int num = (int) DateUtil.betweenDay(beginTime, endTime, true);
        for (int i = 0; i <= num; i++) {
            Date time = DateUtil.offsetDay(beginTime, i).toJdkDate();
            list.add(DateUtil.format(time, "yyyy-MM-dd"));
        }
        return list;
    }

    public static Date buildTimeYYYYMMddHHmm(String yyyy_MM_dd, String HH_mm) {
        String[] yyyy_MM_dds = yyyy_MM_dd.split("-");
        String[] time = HH_mm.split(":");
        return buildTime(Integer.parseInt(yyyy_MM_dds[0]), Integer.parseInt(yyyy_MM_dds[1]), Integer.parseInt(yyyy_MM_dds[2]),
                Integer.parseInt(time[0]), Integer.parseInt(time[1]), 0);
    }

    public static Date buildTimeYYYYMMdd(String yyyy_MM_dd) {
        String[] yyyy_MM_dds = yyyy_MM_dd.split("-");
        return buildTime(Integer.parseInt(yyyy_MM_dds[0]), Integer.parseInt(yyyy_MM_dds[1]), Integer.parseInt(yyyy_MM_dds[2]),
                0, 0, 0);
    }

    /**
     * 判断是周几 1是周1 7是周天
     *
     * @param thisDate
     * @return
     */
    public static int dayOfWeek(Date thisDate) {
        int week = DateUtil.dayOfWeek(thisDate);
        return (week - 1) == 0 ? 7 : week - 1;
    }


    /**
     * 判断是否为节假日
     *
     * @param date 日期 如："2022-04-05"
     * @return
     */
    public static Boolean isHoliday(String date) {
        // 获取该年份的节假日信息
        JSONArray jsonArray = getHolidayOfYear(date.substring(0, 4));
        System.out.println(jsonArray);
        // 遍历节假日信息
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            // 如果传入的日期存在于该年的节假日中，则返回对应的节假日信息(true为节假日, false为调休工作日)
            if (jsonObject.getStr("date").equals(date)) {
                return jsonObject.getBool("isOffDay");
            }
        }
        // 如果不存在, 则直接返回正常计算(非节假日)
        return null;
    }

    /**
     * 获取指定年份的节假日信息
     *
     * @return
     * @paramyear 年份 如："2022"
     */
    public static JSONArray getHolidayOfYear(String year) {
        // 获取指定年份的url
        String url = "https://cdn.jsdelivr.net/gh/NateScarlet/holiday-cn@master/" + year + ".json";
        // 获取返回结果
        String json = HttpUtil.get(url, 0);
        // 解析返回结果
        JSONObject jsonObject = JSONUtil.parseObj(json);
        return jsonObject.getJSONArray("days");
    }
}
