package org.jeecg.modules.adweb.common.util;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.IntStream;

/**
 * AdWeb时间工具类
 *
 * <p>其它场景，参考 {@link org.jeecg.common.util.DateUtils}
 *
 * @author wfansh
 */
@Slf4j
public class DateUtil {

    /** 标准日期格式(yyyy-MM-dd) */
    public static final String DATE_FORMAT = "yyyy-MM-dd";

    /** 标准时间格式(yyyy-MM-dd HH:mm:ss) */
    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    /** 带时区的时间格式(yyyy-MM-dd HH:mm:ss +00:00) */
    public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss X";

    /** 时间格式(yyyy/MM/dd) */
    public static final String SUBJECT_DATE_FORMAT = "yyyy/MM/dd";

    /** 时间格式(yyyyMMdd) */
    public static final String COMPACT_DATE_FORMAT = "yyyyMMdd";

    /** 时间格式(yyyyMMddHHmmss) */
    public static final String COMPACT_DATE_TIME_FORMAT = "yyyyMMddHHmmss";

    /** 带分钟的事件格式 yyyy-MM-dd HH:mm */
    public static final String DATE_FORMAT_MINUTE = "yyyy-MM-dd HH:mm";

    public static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("Asia/Shanghai");

    /** 日期类型, {@link #getDateRangeByType(String)}方法参数 */
    public static final String DATE_TYPE_TODAY = "today";
    public static final String DATE_TYPE_YESTERDAY = "yesterday";
    public static final String DATE_TYPE_SEVEN_DAYS = "sevenDay";
    public static final String DATE_TYPE_THIRTY_DAYS = "thirtyDay";
    public static final String DATE_TYPE_THIS_WEEK = "thisWeek";
    public static final String DATE_TYPE_LAST_WEEK = "lastWeek";
    public static final String DATE_TYPE_THIS_MONTH = "thisMonth";
    public static final String DATE_TYPE_LAST_MONTH = "lastMonth";
    public static final String DATE_TYPE_ALL_TIME = "allTime";

    /** 取得系统当前日期 */
    public static Date getDate() {
        return new Date();
    }

    /**
     * 根据日期类型返回日期区间
     *
     * <p>左右关闭，如dateType为today时，start = "2024-12-06", end = "2024-12-06"
     *
     * @param dateType 日期类型
     */
    public static Pair<Date, Date> getDateRangeByType(String dateType) {
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();

        Date start = null;
        Date end = null;

        switch (dateType) {
            case DATE_TYPE_TODAY:
                start = getTodayZeroTime(now);
                end = start;
                break;
            case DATE_TYPE_YESTERDAY:
                start = getTodayZeroTime(DateUtil.addDays(now, -1));
                end = start;
                break;
            case DATE_TYPE_SEVEN_DAYS:
                end = getTodayZeroTime(now);
                start = addDays(end, -6);
                break;
            case DATE_TYPE_THIRTY_DAYS:
                end = getTodayZeroTime(now);
                start = addDays(end, -29);
                break;
            case DATE_TYPE_THIS_WEEK:
                calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
                start = getTodayZeroTime(calendar.getTime());
                end = getTodayZeroTime(now);
                break;
            case DATE_TYPE_LAST_WEEK:
                calendar.add(Calendar.DAY_OF_YEAR, -7);
                calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
                start = getTodayZeroTime(calendar.getTime());
                end = addDays(start, 6);
                break;
            case DATE_TYPE_THIS_MONTH:
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                start = getTodayZeroTime(calendar.getTime());
                end = getTodayZeroTime(now);
                break;
            case DATE_TYPE_LAST_MONTH:
                calendar.add(Calendar.MONTH, -1);
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                start = getTodayZeroTime(calendar.getTime());
                calendar.set(
                        Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
                end = getTodayZeroTime(calendar.getTime());
            default:
                // e.g. dateType = 7
                if (StringUtils.isNumeric(dateType)) {
                    end = getTodayZeroTime(now);
                    start = addDays(end, 1 - Integer.parseInt(dateType));
                }
        }

        return Pair.of(start, end);
    }

    /**
     * 获取当日零点
     *
     * @param date
     * @return
     */
    public static Date getTodayZeroTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取明日零点
     *
     * @param date
     * @return
     */
    public static Date getTmrZeroTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 在当前日期上追加N天
     *
     * @param date 当前日期
     * @param daysToAdd 添加天数
     * @return 日期字符串形式
     */
    public static Date addDays(Date date, int daysToAdd) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.DAY_OF_MONTH, daysToAdd);
        return c.getTime();
    }

    /**
     * 在当前日期上追加N年
     *
     * @param date 当前日期
     * @param num 添加年数
     * @return 日期字符串形式
     */
    public static Date addYear(Date date, int num) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.YEAR, num);
        return c.getTime();
    }

    /**
     * 计算两个Date的日期差
     *
     * @param start
     * @param end
     * @return
     */
    public static int diffDays(Date start, Date end) {
        return (int)
                ChronoUnit.DAYS.between(
                        LocalDate.ofInstant(start.toInstant(), DEFAULT_ZONE_ID),
                        LocalDate.ofInstant(end.toInstant(), DEFAULT_ZONE_ID));
    }

    /** 获取start和end之间所有的日期，包含起止两端 */
    public static List<Date> getAllDaysBetween(Date start, Date end) {
        return IntStream.rangeClosed(0, diffDays(start, end))
                .mapToObj(diff -> addDays(start, diff))
                .toList();
    }

    /**
     * 根据指定格式，将Date转化为为字符串
     *
     * @param date
     * @param format
     * @return
     */
    public static String formatDate(Date date, String format) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        dateFormat.setLenient(false);
        return dateFormat.format(date);
    }

    /**
     * 根据指定格式，将字符串解析为Date
     *
     * @param dateStr
     * @param format
     * @return
     * @throws ParseException
     */
    public static Date parseDate(String dateStr, String format) {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(format);
            dateFormat.setLenient(false);
            return dateFormat.parse(dateStr);
        } catch (ParseException e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 比较两个日期的大小
     *
     * @param date1
     * @param date2
     * @return int date1>date2返回1，date1<date2返回-1，date1=date2返回0
     */
    public static int compareDate(Date date1, Date date2) {
        return date1.compareTo(date2);
    }

    /**
     * 秒单位转换为duration字符串，如2小时36分18秒
     *
     * @param durationSeconds
     * @return
     */
    public static String formatDuration(double durationSeconds) {
        if (durationSeconds <= 60d) {
            // 小于60秒时，保留两位小数
            return NumberUtil.formatDecimal(durationSeconds, 2) + "秒";
        }

        Duration duration = Duration.ofSeconds((long) durationSeconds);
        int hours = duration.toHoursPart();
        StringBuilder result = new StringBuilder();
        if (hours > 0) {
            result.append(hours).append("小时");
        }

        return result.append(duration.toMinutesPart())
                .append("分")
                .append(duration.toSecondsPart())
                .append("秒")
                .toString();
    }
}
