package com.trade.common.utils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Calendar;
import java.util.Date;

import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.time.DateFormatUtils;

/**
 * 时间工具类
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    public static final String YYYY = "yyyy";
    public static final String YYYY_MM = "yyyy-MM";
    public static final String YYYYMM = "yyyyMM";
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
    public static final String YYYYMMDD = "yyyyMMdd";
    public static final String YYYYMMDDHHMM = "yyyyMMddHHmm";
    public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    private static final String[] parsePatterns = {
            "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", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"
    };
    public static final char PERIOD_UNIT_YEAR = 'Y';
    public static final char PERIOD_UNIT_MONTH = 'M';
    public static final char PERIOD_UNIT_WEEK = 'W';
    public static final char PERIOD_UNIT_DAY = 'D';
    public static final char PERIOD_UNIT_YEAR_CN = '年';
    public static final char PERIOD_UNIT_MONTH_CN = '月';
    public static final char PERIOD_UNIT_WEEK_CN = '周';
    public static final char PERIOD_UNIT_DAY_CN = '天';

    /**
     * 获取当前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 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) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(format).format(date);
    }

    public static final Date dateTime(final String format, final String ts) {
        if (StrUtil.isEmpty(ts)) {
            return null;
        }
        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;
        }
    }

    /**
     * 获取服务器启动时间
     */
    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)));
    }

    /**
     * 计算时间差
     *
     * @param endDate 最后时间
     * @param startTime 开始时间
     * @return 时间差（天/小时/分钟）
     */
    public static String timeDistance(Date endDate, Date startTime) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - startTime.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 date 日期
     * @return
     */
    public static String getCnameOfAMPM(Date date) {
        if (null == date) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (cal.get(Calendar.HOUR_OF_DAY) >= 12) {
            return ConstantUtil.PM_CNAME;
        } else {
            return ConstantUtil.AM_CNAME;
        }
    }

    /**
     * 设置具体日期中午-->下午临界的时分秒
     *
     * @param date 日期实例
     * @return
     */
    public static Date setTimeToPM(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 11);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * 指定日期加years年
     *
     * @param date 被操作日期
     * @param years 被添加的年数（为负数表示减少年数）
     * @return 返回增加年数后的日期
     */
    public static Date plusYear(Date date, int years) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, years);
        return calendar.getTime();
    }

    /**
     * 指定日期加months月
     *
     * @param date 被操作日期
     * @param months 被添加的月数（为负数表示减少月数）
     * @return 返回增加月数后的日期
     */
    public static Date plusMonth(Date date, int months) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    /**
     * 指定日期加days天
     *
     * @param date 被操作日期
     * @param days 被添加的天数（为负数表示减少天数）
     * @return 返回增加天数后的日期
     */
    public static Date plusDay(Date date, int days) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }

    /**
     * 指定日期加hours小时
     *
     * @param date 被操作日期
     * @param hours 被添加的小时数（为负数表示减少小时数）
     * @return 返回增加小时数后的日期
     */
    public static Date plusHour(Date date, int hours) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, hours);
        return calendar.getTime();
    }

    /**
     * 指定日期加minutes分钟
     *
     * @param date 被操作日期
     * @param minutes 被添加的分钟数（为负数表示减少分钟数）
     * @return 返回增加分钟数后的日期
     */
    public static Date plusMinute(Date date, int minutes) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, minutes);
        return calendar.getTime();
    }

    /**
     * 指定日期加seconds秒
     *
     * @param date 被操作日期
     * @param seconds 被添加的秒数（为负数表示减少秒数）
     * @return 返回增加秒数后的日期
     */
    public static Date plusSecond(Date date, int seconds) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

    public static Date atStartOfDay(Date date) {
        if (date == null) {
            return null;
        }
        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();
    }

    public static Date atEndOfDay(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    public static Date atStartOfMonth(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        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();
    }

    public static Date atEndOfMonth(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    public static boolean onTheSameDay(Date date1, Date date2) {
        if (date1 == null) {
            return date2 == null;
        } else if (date2 == null) {
            return false;
        }
        return DateUtils.atStartOfDay(date1).equals(DateUtils.atStartOfDay(date2));
    }

    /**
     * 获取两个时间（最小单位：0.5天）
     * 
     * @param startDate
     * @param endDate
     * @return
     */
    public static String betweenDays(Date startDate, Date endDate) {
        if (null != startDate && null != endDate) {
            long oldTime = startDate.getTime();
            long nowTime = endDate.getTime();
            long poor = 0;
            if (nowTime > oldTime) {
                poor = nowTime - oldTime;
            } else {
                poor = oldTime - nowTime;
            }
            int day = (int) (poor / (1000 * 60 * 60 * 24));
            int hour = (int) (poor / (1000 * 60 * 60) % 24);
            int min = (int) (poor / (1000 * 60) % 60);
            float overdue = 0f;
            if (hour > 11) {
                overdue = 1f;
            }
            if ((hour > 0 && hour <= 11) || (hour == 0 && min > 0)) {
                overdue = 0.5f;
            }
            if (day > 0) {
                overdue = day + overdue;
            }
            return Float.toString(overdue);
        }
        return null;
    }

    /**
     * 获得当天零时零分零秒
     *
     * @return
     */
    public static Date getLastTimeOfTheDay() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

    public static LocalDate longToLocalDate(Long timestamp) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDate parseDate2LocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDateTime parseDate2LocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static Date parseLocalDate2Date(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    public static Date parseLocalDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 转换时间单位为中文
     *
     * @param periodWithUnit 时间单位
     * @return 中文时间单位
     */
    public static String transPeriodUnit(String periodWithUnit) {
        if (StringUtils.isBlank(periodWithUnit)) {
            return periodWithUnit;
        }
        switch (periodWithUnit.charAt(periodWithUnit.length() - 1)) {
            case PERIOD_UNIT_YEAR:
                return periodWithUnit.substring(0, periodWithUnit.length() - 1) + PERIOD_UNIT_YEAR_CN;
            case PERIOD_UNIT_MONTH:
                return periodWithUnit.substring(0, periodWithUnit.length() - 1) + PERIOD_UNIT_MONTH_CN;
            case PERIOD_UNIT_WEEK:
                return periodWithUnit.substring(0, periodWithUnit.length() - 1) + PERIOD_UNIT_WEEK_CN;
            case PERIOD_UNIT_DAY:
                return periodWithUnit.substring(0, periodWithUnit.length() - 1) + PERIOD_UNIT_DAY_CN;
            default:
                return periodWithUnit;
        }
    }

    /**
     * 根据身份证号获取年龄
     *
     * @param idCardNo 身份证号
     * @return 年龄
     */
    public static int getAgeByIdCardNo(String idCardNo) {
        if (StringUtils.isBlank(idCardNo)) {
            return 0;
        }
        int year = Integer.parseInt(idCardNo.substring(6, 10));
        int month = Integer.parseInt(idCardNo.substring(10, 12));
        int day = Integer.parseInt(idCardNo.substring(12, 14));
        LocalDate birthday = LocalDate.of(year, month, day);
        LocalDate now = LocalDate.now();
        Period period = Period.between(birthday, now);
        return period.getYears();
    }
}
