package com.fs.common.utils.date;

import com.fs.common.exception.base.BaseException;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.function.LongFunction;

/**
 * 时间工具类
 * @ClassName: DateUtil.java
 * @author MengDaNai
 * @version 1.0
 * @Date 2019年7月19日 下午5:40:37
 */
@Component
public final class DateUtil {
	/**
	 * 日期格式化 YYYY-MM-dd HH:mm:ss
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM_DD_HH_MM_SS = "YYYY-MM-dd HH:mm:ss";
	/**
	 * 日期格式化 YYYY-MM-dd HH:mm
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM_DD_HH_MM = "YYYY-MM-dd HH:mm";
	/**
	 * 日期格式化 YYYY-MM-dd
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM_DD = "YYYY-MM-dd";
	/**
	 * 日期格式化 YYYY-MM
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM = "YYYY-MM";
	/**
	 * 日期格式化 HH:mm:ss
	 * @Date 2019/7/11 19:56
	 */
	public static final String HH_MM_SS = "HH:mm:ss";
	/**
	 * 日期格式化 HH:mm
	 * @Date 2019/7/11 19:56
	 */
	public static final String MM_MM = "HH:mm";
	/**
	 * 日期格式化 YYYY年MM月dd日 HH时mm分ss秒
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM_DD_HH_MM_SS_TEXT = "YYYY年MM月dd日 HH时mm分ss秒";
	/**
	 * 日期格式化 YYYY年MM月dd日 HH时mm分
	 * @Date 2019/7/11 19:56
	 */
	public static final String YYYY_MM_DD_HH_MM_TEXT = "YYYY年MM月dd日 HH时mm分";
	/**
	 * 日期格式化 HH时mm分ss秒
	 * @Date 2019/7/11 19:56
	 */
	public static final String HH_MM_SS_TEXT = "HH时mm分ss秒";
	/**
	 * 日期格式化 HH时mm分
	 * @Date 2019/7/11 19:56
	 */
	public static final String MM_MM_TEXT = "HH时mm分";

	private static final int DATE_STRING_LENGTH_MAX = 19;
	private static final int DATE_STRING_LENGTH_MIN = 10;

	private DateUtil(){}

	/**
	 * 获取当前时间
	 * @author MengDaNai
	 * @Date 2019年7月19日 下午5:40:50
	 * @return String
	 */
	public static String getDateTime() {
		LocalDateTime datetime = LocalDateTime.now();
		DateTimeFormatter sdf = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
		return datetime.format(sdf);
	}

	/**
	 * 获取当前时间
	 * @author MengDaNai
	 * @Date 2019年7月19日 下午5:41:00
	 * @param pattern 时间格式 yyyy-MM-dd HH:mm:ss
	 * @return String
	 */
	public static String getDateTime(String pattern) {
		LocalDateTime datetime = LocalDateTime.now();
		DateTimeFormatter sdf = DateTimeFormatter.ofPattern(pattern);
		return datetime.format(sdf);
	}

	/**
	 * 获取当前时间
	 * @author MengDaNai
	 * @Date 2019年7月19日 下午5:41:15
	 * @return Date
	 */
	public static Date getDate() {
		String dateTime = getDateTime();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		LocalDateTime startTime=LocalDateTime.parse(dateTime, formatter);
		ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdtStart = startTime.atZone(zoneId);
    	return Date.from(zdtStart.toInstant());
	}

	public static String formatLocalDate(LocalDate localDate){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		return formatter.format(localDate);
	}

	public static String format(String formatterStr){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatterStr);
		return formatter.format(LocalDateTime.now());
	}

	public static String formatLocalDateTime(LocalDateTime localDateTime){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		return formatter.format(localDateTime);
	}

	public static String formatLocalDate(LocalDate localDate, String pattern){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		return formatter.format(localDate);
	}

	public static String formatLocalDateTime(LocalDateTime localDateTime, String pattern){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		return formatter.format(localDateTime);
	}

	/**
	 * StringToDate
	 * @author MengDaNai
	 * @Date 2019年7月19日 下午5:41:22
	 * @param dateTime
	 * @return Date
	 */
	public static Date stringToDate(String dateTime,String pattern) {
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		if(dateTime.length() > DATE_STRING_LENGTH_MAX){
			dateTime = dateTime.substring(0, DATE_STRING_LENGTH_MAX);
		}
		if(dateTime.length() == DATE_STRING_LENGTH_MIN){
			dateTime = dateTime+" 00:00:00";
		}
		LocalDateTime startTime=LocalDateTime.parse(dateTime, formatter);
		ZoneId zoneId = ZoneId.systemDefault();
		ZonedDateTime zdtStart = startTime.atZone(zoneId);
		return Date.from(zdtStart.toInstant());
	}
	public static Date stringToDate(String dateTime) {
		return stringToDate(dateTime,"yyyy-MM-dd HH:mm:ss");
	}
	/**
	 * date转字符串
	 * @author 吴树波
	 * @Date 2019/10/14 10:42
	 * @param: dates 时间
	 * @param: pattern 格式
	 * @Return java.lang.String
	 */
	public static String dateToString(Date date,String pattern){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
		return simpleDateFormat.format(date);
	}
	/**
	 * 字符串转LocalDateTime
	 * @author 吴树波
	 * @Date 2019/10/9 11:39
	 * @param: date 时间字符串
	 * @Return
	 */
	public static LocalDateTime stringToLocalDateTime(String date){
		return dateToLocalDateTime(stringToDate(date));
	}
	public static LocalDateTime localDateToLocalDateTime(LocalDate date){
		return stringToLocalDateTime(getLocalDateStr(date, "yyyy-MM-dd") + " 00:00:00");
	}
	/**
	 * 字符串转LocalDate
	 * @author 吴树波
	 * @Date 2019/10/9 11:39
	 * @param: date 时间字符串
	 * @Return
	 */
	public static LocalDate stringToLocalDate(String date){
		return dateToLocalDate(stringToDate(date));
	}
	/**
	 * 获取一个月多少天
	 * @author 吴树波
	 * @Date 2019年7月19日 下午5:41:32
	 * @param date
	 * @return int
	 */
	public static int getDayByMonth(Date date) {
		return dateToLocalDate(date).lengthOfMonth();
	}

	/**
	 * 获取时间是星期几
	 * @author 吴树波
	 * @Date 2019/7/18 14:27
	 * @param date
	 * @Return java.time.DayOfWeek 枚举类型
	 * @See java.time.DayOfWeek 定义
	 */
	public static DayOfWeek getDayOfWeek(Date date) {
		return dateToLocalDate(date).getDayOfWeek();
	}

	/**
	 * 把Date转换为LocalDateTime
	 * @author 吴树波
	 * @Date 2019/7/18 11:43
	 * @param date
	 * @Return java.time.LocalDateTime
	 */
	public static LocalDateTime dateToLocalDateTime(Date date){
		Instant instant = date.toInstant();
		ZoneId zone = ZoneId.systemDefault();
		return LocalDateTime.ofInstant(instant,zone);
	}

	/**
	 * 把Date转换为localDate
	 * @author 吴树波
	 * @Date 2019/7/18 11:41
	 * @param date
	 * @Return java.time.LocalDate
	 */
	private static LocalDate dateToLocalDate(Date date){
		return dateToLocalDateTime(date).toLocalDate();
	}

	/**
	 * 计算时间差
	 * @author 吴树波->MengDaNai
	 * @Date 2019/7/18 14:14
	 * @param oldTime 过去时间
	 * @param nowTime 当前时间
	 * @param type 要计算的时间差类型
	 * @Return long 技术按结果
	 */
	public static long differenceTime(Date oldTime, Date nowTime, TimeTypeEnum type){
		LocalDateTime oldLocalDateTime = dateToLocalDateTime(oldTime);
		LocalDateTime nowLocalDateTime = dateToLocalDateTime(nowTime);
		return differenceTime(oldLocalDateTime, nowLocalDateTime, type);
	}
	public static long differenceTime(LocalDateTime oldLocalDateTime, LocalDateTime nowLocalDateTime, TimeTypeEnum type){
		switch (type) {
			case YEAR:
				return oldLocalDateTime.until(nowLocalDateTime, ChronoUnit.YEARS);
			case MONTH:
				return oldLocalDateTime.until(nowLocalDateTime, ChronoUnit.MONTHS);
			case DAY:
				return Duration.between(oldLocalDateTime, nowLocalDateTime).toDays();
			case HOUR:
				return Duration.between(oldLocalDateTime, nowLocalDateTime).toHours();
			case MINUTE:
				return Duration.between(oldLocalDateTime, nowLocalDateTime).toMinutes();
			case SECOND:
				return Duration.between(oldLocalDateTime, nowLocalDateTime).toMillis()/1000;
			case MILLI:
				return Duration.between(oldLocalDateTime, nowLocalDateTime).toMillis();
			default:
				throw new BaseException("");
		}
	}
	public static long differenceTime(LocalDate oldLocalDate, LocalDate nowLocalDate, TimeTypeEnum type){
		switch (type) {
			case YEAR:
				return oldLocalDate.until(nowLocalDate, ChronoUnit.YEARS);
			case MONTH:
				return oldLocalDate.until(nowLocalDate, ChronoUnit.MONTHS);
			case DAY:
				return oldLocalDate.until(nowLocalDate, ChronoUnit.DAYS);
			default:
				throw new BaseException("");
		}
	}

	/**
	 * 比较日期大小
	 * <p>
	 * 		time1 = "2018-01-01 00:00:00"
	 * 		time2 = "2019-01-01 00:00:00"
	 * 		return true
	 * 		时间1小于时间2
	 * </p>
	 * @author 吴树波
	 * @Date 2019/7/18 14:22
	 * @param time1
	 * @param time2
	 * @Return boolean
	 */
	public static boolean compareTime(Date time1,Date time2){
		LocalDateTime localDateTime1 = dateToLocalDateTime(time1);
		LocalDateTime localDateTime2 = dateToLocalDateTime(time2);
		return compareTime(localDateTime1,localDateTime2);
	}
	public static boolean compareTime(LocalDateTime time1,LocalDateTime time2){
		return time1.isBefore(time2);
	}
	public static boolean compareTime(LocalDate time1,LocalDate time2){
		return time1.isBefore(time2);
	}

	/**
	 * 对时间加减(不支持乘，除，毫秒级)
	 * @author 吴树波->MengDaNai
	 * @Date 2019/7/18 14:55
	 * @param t 时间
	 * @param calculateTypeEnum 计算的类型
	 * @param num 计算的数量
	 * @param timeTypeEnum 计算的时间类型
	 * @param t 需要返回的类型
	 * @Return java.util.Date
	 * @return
	 */
	public static <T> T calculatingTime(T t, CalculateTypeEnum calculateTypeEnum,
									   long num, TimeTypeEnum timeTypeEnum){
		LocalDateTime localDateTime;
		if(t instanceof Date){
			localDateTime = dateToLocalDateTime((Date) t);
		}else {
			localDateTime = (LocalDateTime) t;
		}
		LocalDateTime nowTime;
		switch (timeTypeEnum){
			case YEAR:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusYears, localDateTime::minusYears);
				break;
			case MONTH:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusMonths , localDateTime::minusMonths);
				break;
			case DAY:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusDays, localDateTime::minusDays);
				break;
			case HOUR:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusHours, localDateTime::minusHours);
				break;
			case MINUTE:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusMinutes, localDateTime::minusMinutes);
				break;
			case SECOND:
				nowTime = calculation(num, calculateTypeEnum, localDateTime::plusSeconds, localDateTime::minusSeconds);
				break;
			default:
				throw new BaseException("");
		}
		return returnTimeType(t,nowTime);
	}

	/**
	 * 执行加还是减
	 * @author 吴树波
	 * @Date 2019/7/18 15:15
	 * @param num 计算数量
	 * @param calculateTypeEnum 计算类型
	 * @param addDeal 加方法
	 * @param subDeal 减方法
	 * @Return java.time.LocalDateTime
	 * @return
	 */
	private static LocalDateTime calculation(long num, CalculateTypeEnum calculateTypeEnum,
								 LongFunction<LocalDateTime> addDeal, LongFunction<LocalDateTime> subDeal) {
		switch (calculateTypeEnum){
			case ADD: return addDeal.apply(num);
			case SUBTRACT: return subDeal.apply(num);
			default:
				throw new BaseException("");
		}
	}
	/**
	 * 更具
	 * @author 吴树波
	 * @Date 2019/11/26 14:48
	 * @param: t
	 * @param: time
	 * @Return T
	 */
	private static <T> T returnTimeType(T t,LocalDateTime localDateTime){
		if(t instanceof Date){
			ZoneId zoneId = ZoneId.systemDefault();
			return (T) Date.from(localDateTime.atZone(zoneId).toInstant());
		}
		if(t instanceof LocalDateTime){
			return (T) localDateTime;
		}
		if(t instanceof LocalDate){
			return t;
		}
		throw new BaseException("类型错误");
	}
	/**
	 * 返回时间字符串  20200101
	 * @author 吴树波
	 * @Date 2020/5/27 9:37
	 * @param date 格式化的时间
	 * @Return java.lang.String
	 */
	public static String getLocalDateStr(LocalDate date) {
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
		return date.format(dateTimeFormatter);
	}
	public static String getLocalDateStr(LocalDate date, String perrt) {
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(perrt);
		return date.format(dateTimeFormatter);
	}
	public static Period difference(LocalDate newDate, LocalDate oldDate){
		return Period.between(newDate, oldDate);
	}

	/**
	 * @Title dateDiffer
	 * @Description 获取日期相差年月日，结束日期会默认加1天比较
	 * @Author dongcan
	 * @Date 2021/1/22 15:41
	 * @param: sdate
	 * @param: edate
	 * @Return java.time.Period
	 */
	public static Period dateDiffer(LocalDate sdate, LocalDate edate) {
		// 如果结束日期是2月底，特殊处理：将日跳到月底那天计算28或29
		// 如不特殊处理，遇到某些情况如：2020-03-29 至 2023-02-28，预期得到2年11月，实际会得到2年11月1天
		if (edate.getMonthValue() == 2 && edate.getDayOfMonth() == edate.lengthOfMonth() && sdate.getDayOfMonth() > edate.getDayOfMonth()) {
			sdate = sdate.minusDays(sdate.getDayOfMonth() - edate.getDayOfMonth());
			edate = edate.minusDays(1);
		}
		return Period.between(sdate, edate.plusDays(1));
	}
	/**
	 * 月份差（2月特殊处理）
	 * @author 吴树波
	 * @date 2021/2/20 11:38
	 * @param sdate 开始时间
	 * @param edate 结束时间
	 * @return java.lang.Long
	 */
	public static Long dateDifferMonth(LocalDate sdate, LocalDate edate) {
		// 如果结束日期是2月底，特殊处理：将日跳到月底那天计算28或29
		// 如不特殊处理，遇到某些情况如：2020-03-29 至 2023-02-28，预期得到2年11月，实际会得到2年11月1天
		if (specialMonth(sdate, edate)) {
			sdate = sdate.minusDays(sdate.getDayOfMonth() - edate.getDayOfMonth());
			edate = edate.minusDays(1);
		}
		return sdate.until(edate.plusDays(1), ChronoUnit.MONTHS);
	}
	/**
	 * 判断是否是特殊月份
	 * @author 吴树波
	 * @date 2021/2/20 11:44
	 * @param sdate 开始时间
	 * @param edate 结束时间
	 * @return boolean
	 */
	public static boolean specialMonth(LocalDate sdate, LocalDate edate) {
		// 如果结束日期是2月底，特殊处理：将日跳到月底那天计算28或29
		// 如不特殊处理，遇到某些情况如：2020-03-29 至 2023-02-28，预期得到2年11月，实际会得到2年11月1天
		return edate.getMonthValue() == 2 && edate.getDayOfMonth() == edate.lengthOfMonth() && sdate.getDayOfMonth() > edate.getDayOfMonth();
	}


	/**
	 * @Title getSdatePlusMonths
	 * @Description 获取起始日加月，减一天(月底除外)
	 * @Author dongcan
	 * @Date 2020/12/25 14:42
	 * @param: sdate
	 * @param: pulusMonth
	 * @Return java.time.LocalDate
	 */
	public static LocalDate getSdatePlusMonths(LocalDate itemSdate, int pulusMonth) {
		LocalDate itemEdate = itemSdate.plusMonths(pulusMonth);
		//System.out.println(DateUtil.formatLocalDate(itemSdate) + " ~ " + DateUtil.formatLocalDate(itemEdate));
		if (itemEdate.getDayOfMonth() == itemEdate.lengthOfMonth() && itemEdate.getDayOfMonth() < itemSdate.getDayOfMonth()) {
			return itemEdate;
		}
		itemEdate = itemEdate.minusDays(1);
		return itemEdate;
	}


}
