package com.engine.organization.util;

import com.engine.organization.common.LocalDateRange;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import weaver.general.BaseBean;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Slf4j
public class OrganizationDateUtil {

    public static final ZoneId CTT = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"));
    public static final ZoneOffset SHANGHAI_ZONE_OFF_SET = ZoneOffset.ofHours(8);

    public static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd");
    public static final FastDateFormat DATETIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");

    public static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static final String MONTH_FORMATTER_PATTERN = "yyyy-MM";
    public static final String DATE_FORMATTER_PATTERN = "yyyy-MM-dd";
    public static final String DATE_TIME_FORMATTER_PATTERN = "yyyy-MM-dd HH:mm:ss";
    /**
     * yyyy-MM
     **/
    private static final String MONTH_REGEX = "^([1-9]\\d{3})-(([0]{0,1}[1-9])|([1][0-2]))$";
    /**
     * yyyy-MM-dd
     **/
    private static final String DAY_REGEX = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$";
    /**
     * 含斜杠日期格式
     */
    private static final String DAY_BAR_REGEX = "^[1-9]\\d{3}/([1-9]|1[0-2])/([1-9]|[1-2][0-9]|3[0-1])$";

    public static Long localDate2EpochMilli(LocalDate localDate) {
        if (localDate == null) {
            return NumberUtils.LONG_ZERO;
        }
        return localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    public static Long localDateTime2EpochMilli(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return NumberUtils.LONG_ZERO;
        }
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    public static Long localDateTime2EpochMilli(Date localDateTime) {
        if (localDateTime == null) {
            return NumberUtils.LONG_ZERO;
        }
        return localDateTime.getTime();
    }

    public static String getFormatYearMonth(LocalDate localDate) {
        if (localDate == null) {
            return StringUtils.EMPTY;
        }
        try {
            return localDate.format(MONTH_FORMATTER);
        } catch (Exception e) {
            log.warn("格式化月份错误", e);
            return StringUtils.EMPTY;
        }
    }


    public static String getFormatYearMonth(Date localDate) {
        if (localDate == null) {
            return StringUtils.EMPTY;
        }
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(MONTH_FORMATTER_PATTERN);
            return simpleDateFormat.format(localDate);
        } catch (Exception e) {
            log.warn("格式化月份错误", e);
            return StringUtils.EMPTY;
        }
    }

    public static String getFormatLocalDate(LocalDate localDate) {
        if (localDate == null) {
            return StringUtils.EMPTY;
        }
        try {
            return localDate.format(DATE_FORMATTER);
        } catch (Exception e) {
            log.warn("格式化日期错误", e);
            return StringUtils.EMPTY;
        }
    }

    public static String getFormatLocalDate(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        try {
            return localDateTime.format(DATE_FORMATTER);
        } catch (Exception e) {
            log.warn("格式化日期错误", e);
            return StringUtils.EMPTY;
        }
    }

    public static String getFormatLocalDateTime(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        try {
            return localDateTime.format(DATE_TIME_FORMATTER);
        } catch (Exception e) {
            log.warn("格式化日期错误", e);
            return StringUtils.EMPTY;
        }
    }

    public static String getFormatLocalDateTime(Date localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_TIME_FORMATTER_PATTERN);
            return simpleDateFormat.format(localDateTime);
        } catch (Exception e) {
            log.warn("格式化日期错误", e);
            return StringUtils.EMPTY;
        }
    }

    public static LocalDateTime dateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

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

    public static Date localDateToDate(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
        return Date.from(zonedDateTime.toInstant());
    }

    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        if (null == localDateTime) {
            return null;
        }
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static String getFormatLocalDate(Date date) {
        if (date == null) {
            return StringUtils.EMPTY;
        }
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        return getFormatLocalDate(localDateTime);
    }

    public static YearMonth localDate2YearMonth(Date localDate) {
        if (localDate == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(localDate);
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH) + 1;
        return YearMonth.of(year, month);
    }

    public static YearMonth String2YearMonth(String localDate) {
        if (checkDay(localDate)) {
            return null;
        }

        return YearMonth.parse(localDate);
    }

    public static LocalDateRange localDate2Range(Date localDate) {
        if (localDate == null) {
            return null;
        }
        return LocalDateRange.builder()
                .fromDate(getFirstDayDateOfMonth(localDate))
                .endDate(getLastDayOfMonth(localDate))
                .build();
    }

    public static LocalDateRange localDate2YearRange(Date localDate) {
        if (localDate == null) {
            return null;
        }
        return LocalDateRange.builder()
                .fromDate(getFirstDayDateOfYear(localDate))
                .endDate(getLastDayOfYear(localDate))
                .build();
    }

    public static Date getFirstDayDateOfMonth(final Date date) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int last = cal.getActualMinimum(Calendar.DAY_OF_MONTH);
        cal.set(Calendar.DAY_OF_MONTH, last);
        return cal.getTime();
    }

    public static Date getLastDayOfMonth(final Date date) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int last = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        cal.set(Calendar.DAY_OF_MONTH, last);
        return cal.getTime();
    }

    public static Date getFirstDayDateOfYear(final Date date) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int last = cal.getActualMinimum(Calendar.DAY_OF_YEAR);
        cal.set(Calendar.DAY_OF_YEAR, last);
        return cal.getTime();
    }

    public static Date getLastDayOfYear(final Date date) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int last = cal.getActualMaximum(Calendar.DAY_OF_YEAR);
        cal.set(Calendar.DAY_OF_YEAR, last);
        return cal.getTime();
    }

    public static String getMonthBegin(String specifiedDay) {
        int year;
        int month;
        Pattern pattern = Pattern.compile("\\d+-\\d+");
        Matcher matcher = pattern.matcher(specifiedDay);
        if (StringUtils.isEmpty(specifiedDay) || !matcher.matches()) {
            return null;
        } else {
            year = Integer.parseInt(specifiedDay.split("-")[0]);
            month = Integer.parseInt(specifiedDay.split("-")[1]);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        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);
        Date startDate = calendar.getTime();
        return sdf.format(startDate);
    }

    public static String getYearMonth(int yearNum, int monthNum) {
        LocalDateTime dateTime = LocalDateTime.now();
        int year = dateTime.getYear() + yearNum;
        int month = dateTime.getMonthValue() + monthNum;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        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);
        Date startDate = calendar.getTime();
        return sdf.format(startDate);
    }

    /**
     * 检查年月格式
     *
     * @param yearMonth
     * @return
     */
    public static boolean checkYearMonth(String yearMonth) {
        return Pattern.matches(MONTH_REGEX, yearMonth);
    }

    /**
     * 检查日期格式
     *
     * @param day
     * @return
     */
    public static boolean checkDay(String day) {
        return Pattern.matches(DAY_REGEX, day) || Pattern.matches(DAY_BAR_REGEX, day);
    }

    public static Date parse(String date, String pattern) {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            return format.parse(date);
        } catch (ParseException e) {
            new BaseBean().writeLog(String.format("日期解析异常:  %s,  %s", date, pattern));
        }
        return null;
    }


    /**
     * LocalDate转YearMonth
     *
     * @param localDate
     * @return
     */
    public static YearMonth toYearMonth(LocalDate localDate) {
        Objects.requireNonNull(localDate, "localDate");
        return YearMonth.of(localDate.getYear(), localDate.getMonthValue());
    }


    /**
     * YearMonth转Date
     * 注意dayOfMonth范围：1到31之间，最大值根据月份确定特殊情况，如2月闰年29，非闰年28
     * 如果要转换为当月最后一天，可以使用下面方法：toDateEndOfMonth(YearMonth)
     *
     * @param yearMonth
     * @param dayOfMonth
     * @return
     */
    public static Date toDate(YearMonth yearMonth, int dayOfMonth) {
        Objects.requireNonNull(yearMonth, "yearMonth");
        return localDateToDate(yearMonth.atDay(dayOfMonth));
    }

    /**
     * YearMonth转Date，转换为当月第一天
     *
     * @param yearMonth
     * @return
     */
    public static Date toDateStartOfMonth(YearMonth yearMonth) {
        return toDate(yearMonth, 1);
    }

    /**
     * YearMonth转Date，转换为当月最后一天
     *
     * @param yearMonth
     * @return
     */
    public static Date toDateEndOfMonth(YearMonth yearMonth) {
        Objects.requireNonNull(yearMonth, "yearMonth");
        return localDateToDate(yearMonth.atEndOfMonth());
    }


    /**
     * YearMonth转LocalDate
     * 注意dayOfMonth范围：1到31之间，最大值根据月份确定特殊情况，如2月闰年29，非闰年28
     * 如果要转换为当月最后一天，可以使用下面方法：toLocalDateEndOfMonth(YearMonth)
     *
     * @param yearMonth
     * @param dayOfMonth
     * @return
     */
    public static LocalDate toLocalDate(YearMonth yearMonth, int dayOfMonth) {
        Objects.requireNonNull(yearMonth, "yearMonth");
        return yearMonth.atDay(dayOfMonth);
    }

    /**
     * YearMonth转LocalDate，转换为当月第一天
     *
     * @param yearMonth
     * @return
     */
    public static LocalDate toLocalDateStartOfMonth(YearMonth yearMonth) {
        return toLocalDate(yearMonth, 1);
    }

    /**
     * YearMonth转LocalDate，转换为当月最后一天
     *
     * @param yearMonth
     * @return
     */
    public static LocalDate toLocalDateEndOfMonth(YearMonth yearMonth) {
        Objects.requireNonNull(yearMonth, "yearMonth");
        return yearMonth.atEndOfMonth();
    }

    /**
     * String转Date
     *
     * @param date
     * @return
     */
    public static Date stringToDateTime(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date parse = null;
        if (date != null) {
            try {
                parse = sdf.parse(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
        return parse;
    }

    public static Date stringToDate(String date) {
        return dateStrToLocalDate(date);
    }

    //格式化日期
    public static String strToDateLong(String strDate) {
        Date date = new Date();
        try {
            date = new SimpleDateFormat("yyyyMMddHHmmss").parse(strDate + "000000");//先按照原格式转换为时间
        } catch (ParseException e) {
            e.printStackTrace();
        }
        String str = new SimpleDateFormat("yyyy-MM").format(date);//再将时间转换为对应格式字符串
        return str;
    }

    private static Date dateStrToLocalDate(String date) {
        Date localDate = null;
        try {
            date = date.substring(0, 10);
            if (date.contains("/")) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
                localDate = format.parse(date);
            } else if (date.contains("-")) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                localDate = format.parse(date);
            }
        } catch (Exception e) {
            log.error("日期解析异常,{}", date);
            localDate = null;
        }

        return localDate;
    }
}


