package com.smppw.utils;

import com.smppw.constants.DateConst;
import com.smppw.common.pojo.enums.Frequency;
import com.smppw.common.pojo.enums.TimeRange;
import com.smppw.common.rollrange.date.DateConvertor;
import com.smppw.common.rollrange.date.DateConvertorImpl;
import com.smppw.common.rollrange.date.DateVerifier;
import com.smppw.common.rollrange.date.PeriodPosition;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.*;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class DateUtil {

    public static final Map<Integer,String> charMap = new HashMap();
    public static final Pattern p = Pattern.compile("^(\\d+)\\D*(\\d*)\\D*(\\d*)\\D*(\\d*)\\D*(\\d*)\\D*(\\d*)");
    static {

        charMap.put(1, "y");

        charMap.put(2, "M");

        charMap.put(3, "d");

        charMap.put(4, "H");

        charMap.put(5, "m");

        charMap.put(6, "s");

    }

    public static Comparator<String> dateCmp = new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }
    };

    public static String generateStartDate(String endDate, TimeRange timeRange, Frequency frequency) {
        if(timeRange == null) return null;

        if (timeRange == TimeRange.FromSetup)
            return null;
        String dailyStart = RangeTime(timeRange.getFloatDateSymbel(), endDate);

        if(TimeRange.Last1Week == timeRange)
            return dailyStart;

        String verifiedStartDate = DateVerifier.verifyPeriodStart(dailyStart, frequency);
        return verifiedStartDate;
    }

    public static String RangeTime(String dateSymbel, String relDate) {
        String re = "[dwmqsy]a[+-][0-9]+";
        if (dateSymbel != null && Pattern.matches(re, dateSymbel)) {
            Frequency freq = Frequency.getFrequency(String.valueOf(dateSymbel.charAt(0)));
            Integer count = Integer.valueOf(dateSymbel.substring(2, dateSymbel.length()));

            Frequency validFreq = null;
            if (count == 0)
                validFreq = freq;
            else {
                if (freq == Frequency.Daily || freq == Frequency.Weekly) {
                    validFreq = freq;
                } else {
                    validFreq = Frequency.Monthly;
                    int monthPerYear = 12;
                    count *= monthPerYear / (int) freq.getNum();
                }
            }
            DateConvertor dc = new DateConvertorImpl();
            String periodEnd = dc.setPeriodLast(relDate, validFreq);
            String periodStart = dc.setPeriodFirst(relDate, validFreq);

            String targetDate = null;
            if (count == 0)
                targetDate = dc.addPeriod(relDate, count, validFreq, PeriodPosition.PeriodFirst, false);
            else {
                if (relDate.equals(periodEnd) && count < 0) {
                    targetDate = dc.addPeriod(relDate, count + 1, validFreq, PeriodPosition.PeriodFirst, false);
                } else if (relDate.equals(periodStart) && count > 0) {
                    targetDate = dc.addPeriod(relDate, count - 1, validFreq, PeriodPosition.PeriodLast, false);
                } else {
                    targetDate = dc.addPeriod(relDate, count, validFreq, PeriodPosition.PeriodFixed, false);
                    targetDate = dc.addPeriod(targetDate, count < 0 ? 1 : -1, Frequency.Daily,
                            PeriodPosition.PeriodFixed, false);
                }
            }

            return targetDate;
        }
        return dateSymbel;
    }


    /**
     * 自适应String转Date
     * @param dateString 入参
     * @return  返回Date
     */
    public static Date StringToDate(String dateString) {
        dateString.trim().replaceAll("[a-zA-Z]"," ");

        if(Pattern.matches("^[-+]?\\d{13}$",dateString)) {//支持13位时间戳
            return new Date(Long.parseLong(dateString));
        }
        Matcher m = p.matcher(dateString);
        StringBuilder sb = new StringBuilder(dateString);
        if (m.find(0)) {//从被匹配的字符串中，充index = 0的下表开始查找能够匹配pattern的子字符串。m.matches()的意思是尝试将整个区域与模式匹配，不一样。
            int count = m.groupCount();
            for (int i = 1; i <= count; i++) {
                for (Map.Entry<Integer,String> entry : charMap.entrySet()) {
                    if (entry.getKey() == i) {
                        sb.replace(m.start(i), m.end(i), replaceEachChar(m.group(i), entry.getValue()));
                    }
                }
            }
        } else {
            log.error("错误的日期格式"+dateString);
        }
        Date date = null;
        try {
            date = new SimpleDateFormat(sb.toString()).parse(dateString);
        } catch (ParseException e) {
        }
        return date;
    }


    public static String getAroundDate(Date date, int offnum) {
        Calendar numday = Calendar.getInstance();
        numday.setTime(date);
        numday.add(Calendar.DATE, offnum);
        return (new SimpleDateFormat("yyyy-MM-dd")).format(numday.getTime());
    }

    public static String replaceEachChar(String s, String c) {
        StringBuilder sb = new StringBuilder("");

        for (Character c1 : s.toCharArray()) {
            if (c1 != ' ') {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     *
     * @param date   时间
     * @param pattern   格式
     * @return  日期
     */
    public static String DateToString(Date date, String pattern) {
        DateFormat sdf = new SimpleDateFormat(pattern);
        String strDate = "";
        try {
            strDate = sdf.format(date);
        } catch (Exception e) {
            log.error("时间转换报错", e.getMessage(), e);
        }
        return strDate;
    }

    /**
     *
     * @param strDate   字符串类型的时间
     * @param pattern   格式
     * @return  日期
     */
    public static Date StringToDate(String strDate, String pattern) {
        Date date = null;
        try {
            date = new SimpleDateFormat(pattern).parse(strDate);
        } catch (ParseException e) {
            log.error("时间转换报错", e.getMessage(), e);
        }
        return date;
    }



    public static Calendar DateToCalendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    public static int getDateDistance(String startDate, String endDate) {
        return getDateDistance(startDate, endDate, Frequency.Daily, false);
    }

    public static int getDateDistance(String startDate, String endDate, Frequency freq, boolean isRange) {
        DateTime dt1 = new DateTime(startDate, DateTimeZone.forID("+08:00"));
        DateTime dt2 = new DateTime(endDate, DateTimeZone.forID("+08:00"));
        if (DateConst.YYYY_MM.equals(DateUtil.getDatePattern(startDate))) {
            dt1 = dt1.dayOfMonth().withMinimumValue();
            dt2 = dt2.dayOfMonth().withMaximumValue();
        }
        if (isRange) {
            dt2 = dt2.plusDays(1);
        }
        int distance = -1;
        if (freq == Frequency.Daily) {
            distance = Days.daysBetween(dt1, dt2).getDays();
        } else if (freq == Frequency.Monthly) {
            distance = Months.monthsBetween(dt1, dt2).getMonths();
        } else if (freq == Frequency.Annually) {
            distance = Years.yearsBetween(dt1, dt2).getYears();
        } else if (freq == Frequency.Weekly) {
            distance = Weeks.weeksBetween(dt1, dt2).getWeeks();
        }
        return Math.abs(distance);
    }

    public static String getDatePattern(String date) {
        String patternYMD = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
        String patternYM = "[0-9]{4}-[0-9]{2}";
        String patternY = "[0-9]{4}";
        String patternWeek = "[0-9]{6}";

        if (Pattern.matches(patternYMD, date)) {
            return DateConst.YYYY_MM_DD;
        } else if (Pattern.matches(patternYM, date)) {
            return DateConst.YYYY_MM;
        } else if (Pattern.matches(patternY, date)) {
            return DateConst.YYYY;
        }else if(Pattern.matches(patternWeek,date)){
            return DateConst.YYYYWW;
        }
        return null;
    }

    /**
     * 获取当前日期 yyyy-MM-dd
     *
     * @return yyyy-MM-dd
     */
    public static String getTodayDate() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(date);
    }

    /**
     * 获取日期是当月的第几天
     *
     * @return 2
     */
    public static int getDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取日期所在年份
     *
     * @return 2
     */
    public static int getYear(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    /**
     * 对日期操作，增加N年，如果为负数则为减去N年
     * @param date 日期
     * @param amount 增加的年份，负数代表减去
     * @param pattern 日期格式
     * @return 返回和原格式一样的String值
     */
    public static String addYear(String date, int amount, String pattern) {
        if (amount == 0) {
            return date;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            Date initDate = sdf.parse(date);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(initDate);
            calendar.add(Calendar.YEAR, amount);
            return sdf.format(calendar.getTime());
        } catch (Exception e) {
            log.error("addMonth format Exception: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 对日期操作，增加N个月，如果为负数则为减去N个月
     * @param date 日期
     * @param amount 增加的月份，负数代表减去
     * @param pattern 日期格式
     * @return 返回和原格式一样的String值
     */
    public static String addMonth(String date, int amount, String pattern) {
        if (amount == 0) {
            return date;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            Date initDate = sdf.parse(date);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(initDate);
            calendar.add(Calendar.MONTH, amount);
            return sdf.format(calendar.getTime());
        } catch (Exception e) {
            log.error("addMonth format Exception: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 对日期操作，增加N天，如果为负数则为减去N天
     * @param date 日期
     * @param amount 增加的月份，负数代表减去
     * @param pattern 日期格式
     * @return 返回Date
     */
    public static Date addDay(String date, int amount, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            Date initDate = sdf.parse(date);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(initDate);
            calendar.add(Calendar.DAY_OF_YEAR, amount);
            return calendar.getTime();
        } catch (Exception e) {
            log.error("addMonth format Exception: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 计算两个日期间的年份差距
     *
     * @param startDate
     * @param endDate
     * @return 返回保留一位的小数格式
     */
    public static String getDateDuration(String startDate, String endDate) {
        DateTime dt1 = new DateTime(startDate, DateTimeZone.forID("+08:00"));
        DateTime dt2 = new DateTime(endDate, DateTimeZone.forID("+08:00"));
        int distance = Days.daysBetween(dt1, dt2).getDays();
        return String.format("%.1f", BigDecimalUtils.divide(new BigDecimal(distance), new BigDecimal(365)).doubleValue());
    }

    public static String getPreDate(String dateStr) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, -1);
        Date preDate = calendar.getTime();
        String preDateStr = DateToString(preDate, DateConst.YYYY_MM_DD);
        return preDateStr;
    }

    /**
     * 获取本月第n天
     * @return
     */
    public static String getThisMonthNumDate(String dateStr, Integer n) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, n);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);

        Date time = cal.getTime();
        return DateToString(time, DateConst.YYYY_MM_DD);
    }

    /**
     * 获取月份 - 1
     * @return
     */
    public static String getBeforeMonth(String dateStr) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, -1);

        Date time = cal.getTime();
        return DateToString(time, DateConst.YYYY_MM_DD);
    }

    /**
     * 获取本月末
     * @return
     */
    public static String getThisMonthEndDate(String dateStr) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));

        Date time = cal.getTime();
        return DateToString(time, DateConst.YYYY_MM_DD);
    }

    /**
     * 获取月份 + 1
     * @return
     */
    public static String getLaterMonth(String dateStr) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, 1);

        Date time = cal.getTime();
        return DateToString(time, DateConst.YYYY_MM_DD);
    }

    /**
     * 获取本月初
     * @return
     */
    public static String getThisMonthFirstDate(String dateStr) {
        Date date = StringToDate(dateStr, DateConst.YYYY_MM_DD);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);

        Date time = cal.getTime();
        return DateToString(time, DateConst.YYYY_MM_DD);
    }

    public static int toMonthNumber(String latestDate) {
        Date date = StringToDate(latestDate, DateConst.YYYY_MM);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        return year * 12 + month - 1;
    }
}
