package com.tzy.common.utils;

import com.alibaba.fastjson.JSONObject;
import com.tzy.common.constant.Constants;
import com.tzy.common.exception.ServiceException;
import com.tzy.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * 时间工具类
 *
 * @author zheng
 */
@Slf4j
@Component
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

	public static String add = "ADD";
	public static String cut = "CUT";
	public static String year = "year";
	public static String month = "month";
	public static String day = "day";
	public static String hour = "hour";
	public static String minute = "minute";
	public static String second = "second";
	//秒/天
	public static Integer day2second = 24 * 60 * 60;
	//秒/小时
	public static Integer hour2second = 1 * 60 * 60;
	//秒/分钟
	public static Integer minute2second = 1 * 60;


	public static String YYYY = "yyyy";

	private static String MM = "MM";

	public static String YYYY_MM = "yyyy-MM";

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

	public static String MM_DD = "MM.dd";

    public static String YYYYMMDD = "yyyyMMdd";
    public static String HHMMSS = "HHmmss";

	public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

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

	private static JSONObject holiday = new JSONObject();

	private static 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"};

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

	public static final Date dateTime(final String format, final String ts) {
		try {
			if (StringUtils.isEmpty(ts)) return null;
			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 dateStr
	 * @return
	 */
	public static boolean isDate(String dateStr , String format){
		try{
			Date date = dateTime(format, dateStr);
			String endStr = parseDateToStr(format, date);
			return endStr.equals(dateStr);
		}catch (Exception e){

		}
		return false;
	}

	/**
	 * 计算两个时间差
	 */
	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 + "分钟";
	}

	/**
	 * 计算两个时间差多少秒
	 */
	public static Long getMinPoorSec(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;
		// 计算差多少秒//输出结果
		return diff / ns;
	}

	/**
	 * 时间操作
	 *
	 * @param date 时间
	 * @param type 操作类型 年月日时分秒
	 * @param num  数量
	 * @return
	 */
	public static Date addCalendar(Date date, String type, Integer num) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		switch (type) {
			case "year":
				calendar.add(Calendar.YEAR, num);
				break;
			case "month":
				calendar.add(Calendar.MONTH, num);
				break;
			case "day":
				calendar.add(Calendar.DAY_OF_MONTH, num);
				break;
			case "hour":
				calendar.add(Calendar.HOUR_OF_DAY, num);
				break;
			case "minute":
				calendar.add(Calendar.MINUTE, num);
				break;
			case "second":
				calendar.add(Calendar.SECOND, num);
				break;
		}
		return calendar.getTime();
	}

	/**
	 * 获取指定的工作日
	 *
	 * @param date
	 * @param num
	 * @return
	 * @throws ParseException
	 */
	public static List<Date> getWorkDays(Date date, Integer num) throws ParseException {
		List<Date> workdays = new ArrayList<>();
		for (int i = 0; i < num; i++) {
			date = getNextWorkDay(date);
			workdays.add(date);
		}
		return workdays;
	}

	/**
	 * 获取指定工作天数后的工作日
	 *
	 * @param date
	 * @param num
	 * @return
	 * @throws ParseException
	 */
	public static Date getAfterNumWorkDay(Date date, Integer num) throws ParseException {
		for (int i = 0; i < num; i++) {
			date = getNextWorkDay(date);
		}
		return date;
	}

	/**
	 * 获取指定工作天数后的工作日 - 以当天开始
	 *
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date getNumWorkDayByNow(Date date, Integer num) throws ParseException {
		//加载当前年的国家节假日
		holiday = initRedisHolidays(date);
		if (!isWeekend(date)) num = num - 1;
		for (int i = 0; i < num; i++) {
			date = getNextWorkDay(date);
		}
		return date;
	}

	/**
	 * 获取当前时间的下一个工作日
	 *
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date getNextWorkDay(Date date) throws ParseException {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		Date nextDate = getNextDate(calendar);
		if (!isWeekend(nextDate)) {
			return nextDate;
		} else {
			return getNextWorkDay(nextDate);
		}
	}

	public static Timestamp getTimestampNow() {
		return Timestamp.valueOf(LocalDateTime.now());
	}

	/**
	 * 获取下一日期
	 *
	 * @param calendar
	 * @return
	 */
	public static Date getNextDate(Calendar calendar) {
		calendar.add(Calendar.DAY_OF_MONTH, +1);
		return calendar.getTime();
	}

	/**
	 * 是否是 休息日
	 *
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static boolean isWeekend(Date date) throws ParseException {
		//判断当前时间是否在holidays 内
		String dateTimes = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, date);
		try {
			String type = holiday.getString(dateTimes);
			if (StringUtils.isNotBlank(type) && Objects.equals(type, "休")) {
				return true;
			}
			//判断是否上班 如果上班则不进行 下面周六周日计算
			if (StringUtils.isNotBlank(type) && Objects.equals(type, "班")) {
				return false;
			}
		}catch (Exception e){
			log.error(" 获取 holiday error:{}", e.getMessage());
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 获取指定时间的开始时间
	 *
	 * @param date
	 * @return
	 */
	public static Date getStartTimeByDate(Date date) {
		if (null == date) date = new Date();
		return parseDate(dateTime(date));
	}

	/**
	 * 获取指定时间的开始时间
	 *
	 * @param date
	 * @return
	 */
	public static Date getEndTimeByDate(Date date) {
		if (null == date) date = new Date();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		Date nextDate = getNextDate(calendar);
		date = parseDate(dateTime(nextDate));
		calendar.setTime(date);
		calendar.add(Calendar.SECOND, -1);
		return calendar.getTime();
	}

	/**
	 * 根据身份证号判断是否未成年
	 *
	 * @param certNo 身份证号
	 * @return
	 */
	public static boolean isMinor(String certNo) {
		if (certNo.length() != 18) {
			throw new ServiceException(500, "您好,暂时仅支持二代身份证校验!");
		}

		int year = Integer.valueOf(certNo.substring(6).substring(0, 4));// 得到年份
		int month = Integer.valueOf(certNo.substring(10).substring(0, 2));// 得到月份
		int day = Integer.valueOf(certNo.substring(12).substring(0, 2));//得到日

		LocalDate today = LocalDate.now();
		//System.out.println("Today : " + today);
		LocalDate birthDate = LocalDate.of(year, month, day);
		// System.out.println("BirthDate : " + birthDate);
		Period p = Period.between(birthDate, today);
		//System.out.printf("年龄 : %d 年 %d 月 %d 日", p.getYears(), p.getMonths(), p.getDays());

		return p.getYears() >= 18 && p.getYears() < 70;
	}

	/**
	 * 获取指定几天后的日期
	 * @param date 当前日期
	 * @param day 几天后的日期
	 * @return
	 */
	public static Date getFutureDate(Date date,int day) {
		Calendar calc =Calendar.getInstance();
		calc.setTime(date);
		calc.add(Calendar.DATE, day);
		return calc.getTime();
	}

	public static int getCurrentHour() {
		Calendar calc =Calendar.getInstance();
		return calc.get(Calendar.HOUR_OF_DAY);
	}

	public static int getCurrentDate() {
		Calendar calc =Calendar.getInstance();
		return calc.get(Calendar.DATE);
	}

	public static boolean isFirDayOfMonth(){
		return getCurrentDate()==1;
	}


	/**
	 * 转换ISO日期格式
	 * @param date
	 * @return
	 */
	public static String tranISOStr(Date date) {
		//当前时间转IS0 8601
		TimeZone tz = TimeZone.getTimeZone("GMT-01");
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
		df.setTimeZone(tz);
		return df.format(date);
	}


	/**
	 * 比较2个时间相隔几天
	 * @param befDate
	 * @param aftDate
	 * @return
	 */
	public static int compareDate(Date befDate,Date aftDate) {
		int days = (int) ((aftDate.getTime() - befDate.getTime()) / (1000*3600*24));
		return days;
	}

	/**
	 * 计算相差分钟
	 */
	public static int differentMinutesByMillisecond(Date date1, Date date2) {
		return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 60)));
	}

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

	/**
	 * 获取本月第一天
	 * @return
	 */
	public static Date getFirstDayOfMonth(){
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		cal.set(Calendar.DAY_OF_MONTH, 1);
		return parseDate(DateUtils.parseDateToStr(YYYY_MM_DD,cal.getTime()));
	}

	/**
	 * 获取上个月第X天
	 * @return
	 */
	public static Date getLastMonthDay(int day){
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		cal.add(Calendar.MONTH, -1);
		cal.set(Calendar.DAY_OF_MONTH, day);
		return parseDate(DateUtils.parseDateToStr(YYYY_MM_DD,cal.getTime()));
	}

	// 上个月
	public static String getLastMonthStr(){
		// 获取当前日期
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.MONTH, -1);
		return DateUtils.parseDateToStr(YYYY_MM, calendar.getTime());
	}

	/**
	 * 获取X个月第X天
	 * @return
	 */
	public static Date getLastByMonthDay(int month, int day){
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		cal.add(Calendar.MONTH, month);
		cal.set(Calendar.DAY_OF_MONTH, day);
		return parseDate(DateUtils.parseDateToStr(YYYY_MM_DD,cal.getTime()));
	}

	public static Date getDateByDay(int day){
		// 获取当前日期
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.DAY_OF_MONTH, day);
		return parseDate(DateUtils.parseDateToStr(YYYY_MM_DD, calendar.getTime()));
	}

	/**
	 * data转LocalDateTime
	 * @param date
	 * @return
	 */
	public static LocalDateTime toLocalDateTime(Date date) {
		return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
	}

	/**
	 * 2个时间间隔天数
	 * @param dateBef
	 * @param dateAft
	 * @return
	 */
	public static long intervalDayOfDays(Date dateBef, Date dateAft) {
		return ChronoUnit.DAYS.between(toLocalDateTime(dateBef), toLocalDateTime(dateAft));
	}

	/**
	 * 间隔小时
	 * @param dateBef
	 * @param dateAft
	 * @return
	 */
	public static double intervalHourOfDays(Date dateBef, Date dateAft) {
		//return ChronoUnit.HOURS.between(toLocalDateTime(dateBef), toLocalDateTime(dateAft));
		if (dateBef == null || dateAft == null) {
			throw new IllegalArgumentException("时间不能为空");
		}

		long millisDiff = dateAft.getTime() - dateBef.getTime();
		return millisDiff / (1000.0 * 60 * 60); // 毫秒转小时，保留小数
	}

	/**
	 * 获取当前月和日 格式 5.14
	 * @return
	 */
	public static String getCurMonthDay() {
		Calendar calendar = Calendar.getInstance();
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		return month+ Constants.dot +day;
	}

	/**
	 * 判断生日是否小于18岁
	 * @param birthDate
	 * @return
	 */
	public static boolean isUnder18(Date birthDate) {
		if (birthDate == null) {
			throw new IllegalArgumentException("Birth date cannot be null");
		}

		// 获取当前日期
		Calendar today = Calendar.getInstance();

		// 获取出生日期
		Calendar birthDay = Calendar.getInstance();
		birthDay.setTime(birthDate);

		// 计算年龄
		int age = today.get(Calendar.YEAR) - birthDay.get(Calendar.YEAR);

		// 如果当前日期还未到出生月份，或者同月份但日期未到，年龄减一
		if (today.get(Calendar.MONTH) < birthDay.get(Calendar.MONTH) || (today.get(Calendar.MONTH) == birthDay.get(Calendar.MONTH) && today.get(Calendar.DAY_OF_MONTH) < birthDay.get(Calendar.DAY_OF_MONTH))) {
			age--;
		}

		// 判断是否小于 18 岁
		return age < 18;
	}

	/**
	 * 加载节假日 从redis中
	 * @param date
	 * @return
	 */
	private static JSONObject initRedisHolidays(Date date){
		String year = DateUtils.parseDateToStr(DateUtils.YYYY, date);
		Object redis = SpringUtils.getRedis("holiday_" + year,"hash");
		if(Objects.nonNull(redis)) {
			JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(redis));
			//判断当前时间是否 为12月 如果是12月则默认获取下年的日历
			if(Objects.equals(DateUtils.parseDateToStr(DateUtils.MM,date),"12")){
				Object nextYearRedis = SpringUtils.getRedis("holiday_" + (Integer.parseInt(year)+1),"hash");
				jsonObject.putAll(JSONObject.parseObject(JSONObject.toJSONString(nextYearRedis)));
			}
			return jsonObject;
		}
		return null;
	}


	public static void main(String[] args) throws ParseException {
       /* List<Date> workDays = getWorkDays(new Date(), 5);
        if(!workDays.isEmpty()){
            workDays.forEach(System.out::println);
        }
        Date afterNumWorkDay = getAfterNumWorkDay(new Date(), 5);
       // System.out.println(afterNumWorkDay);
        System.out.println(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,new Date(System.currentTimeMillis())));
        System.out.println(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,new Date(System.currentTimeMillis()+2900*60)));*/
//        System.out.println(getStartTimeByDate(new Date()));
//        System.out.println(getEndTimeByDate(new Date()));
		//System.out.println( isMinor("362502200503045814"));
		//System.out.println(getCurrentHour());
		//System.out.println(DateUtils.getDate());
		//System.out.println(BigDecimal.ONE.divide(new BigDecimal("0.03"),0,BigDecimal.ROUND_HALF_DOWN));
		//System.out.println(DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS,"20220715000000"));
		//System.out.println( differentDaysByMillisecond(DateUtils.parseDate("2021-10-25"),new Date()));
		//System.out.println(tranISOStr(dateTime(YYYY_MM_DD_HH_MM_SS,"2022-10-21 00:15:48")));
		//Calendar cal = Calendar.getInstance();
		//cal.setTime(new Date());
		//cal.set(Calendar.DAY_OF_MONTH, 1);
		//System.out.println( cal.getTime());
		//System.out.println(getFirstDayOfMonth());
		//System.out.println(intervalHourOfDays(new Date(),getFutureDate(new Date(),3)));
		//System.out.println(dateTimeNow(MM_DD));
		//System.out.println(getCurMonthDay());
	}


    public static boolean isBefore(Date registerTime, Date date) {
		return registerTime.before(date);
    }
}
