package com.bocloud.ims.service.utils;

import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import org.springframework.util.StringUtils;

/**
 * 日期工具类
 * 
 * @author sxp
 * @since Nov 14, 2017
 */
public final class DateUtils {
    /**
     * 获取两个时间之间的相差的时间
     * <p>
     * 选择合适的时间单位，舍入计算后所得数值的所有位数为4位，例如：1.000 ns , 1.235 ms
     * <p/>
     *
     * @author sxp
     * @since 2017/11/14
     */
    public static String getElapsedTimeWith4Digits(Date begin, Date end) {
        Preconditions.checkNotNull(begin, "begin");
        Preconditions.checkNotNull(end, "end");

        long millis = end.getTime() - begin.getTime();
        TimeUnit unit = chooseUnit(millis);

        double value = (double) millis / MILLISECONDS.convert(1, unit);
        return String.format("%.4g", value) + chineseUinit(unit);
    }

    private static TimeUnit chooseUnit(long millis) {
        if (DAYS.convert(millis, MILLISECONDS) > 0) {
            return DAYS;
        }
        if (HOURS.convert(millis, MILLISECONDS) > 0) {
            return HOURS;
        }
        if (MINUTES.convert(millis, MILLISECONDS) > 0) {
            return MINUTES;
        }
        if (SECONDS.convert(millis, MILLISECONDS) > 0) {
            return SECONDS;
        }
        return MILLISECONDS;
    }

    private static String chineseUinit(TimeUnit unit) {
        switch (unit) {
        case NANOSECONDS:
            return "纳秒";
        case MICROSECONDS:
            return "微秒";
        case MILLISECONDS:
            return "毫秒";
        case SECONDS:
            return "秒";
        case MINUTES:
            return "分钟";
        case HOURS:
            return "小时";
        case DAYS:
            return "天";
        default:
            return "未知的时间单位";
        }
    }

    /**
     * 将字符串转成时间
     * 
     * @param dateString
     *            时间的字符串，例如：2016-07-22
     * @param format
     *            字符串匹配模式，例如：yyyy-MM-dd
     * @return 返回dateString对应的Date对象
     * @author sxp
     * @since V1.0 Jul 22, 2016
     */
    public static Date stringToDate(String dateString, String format) {
        if (dateString == null || "".equals(dateString)) {
            throw new RuntimeException("传入参数中的[时间串]不能为空！");
        }
        if (format == null || "".equals(format)) {
            throw new RuntimeException("传入参数中的[时间格式]不能为空！");
        }

        SimpleDateFormat df = new SimpleDateFormat(format);
        df.setLenient(false);// 将2017-13-01看做是一个非法的日期字符串

        Date date;

        try {
            date = df.parse(dateString);
        } catch (ParseException e) {
            throw new RuntimeException(e.getMessage());
        }

        return date;
    }

    /**
     * 将时间转成字符串
     * 
     * @param date
     *            时间
     * @param format
     *            字符串匹配模式，例如：yyyy-MM-dd
     * @author sxp
     * @since V1.0 Jul 22, 2016
     */
    public static String dateToString(Date date, String format) {
        if (date == null) {
            return null;
        }
        if (format == null || "".equals(format)) {
            throw new RuntimeException("传入参数中的[时间格式]不能为空！");
        }

        SimpleDateFormat df = new SimpleDateFormat(format);
        df.setLenient(false);// 将2017-13-01看做是一个非法的日期字符串
        return df.format(date);
    }

    /**
     * 判断某个字符串是否为有效的日期
     * 
     * @param dateString
     *            时间的字符串，例如：2016-07-22
     * @param format
     *            字符串匹配模式，例如：yyyy-MM-dd
     * @author sxp
     * @since Dec 27, 2017
     */
    public static boolean isDate(String dateString, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        // 将2017-13-01看做是一个非法的日期字符串
        df.setLenient(false);

        try {
            df.parse(dateString);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }

    /**
     * 将UTC时间字符串转成时间
     *
     * @param dateString
     *            时间的字符串，例如：2016-07-22
     * @param format
     *            字符串匹配模式，例如：yyyy-MM-dd
     * @return 返回dateString对应的Date对象
     * @author sxp
     * @since 2018-05-14
     */
    public static Date utcStringToDate(String dateString, String format) {
        if (dateString == null || "".equals(dateString)) {
            throw new RuntimeException("传入参数中的[时间串]不能为空！");
        }
        if (format == null || "".equals(format)) {
            throw new RuntimeException("传入参数中的[时间格式]不能为空！");
        }

        SimpleDateFormat df = new SimpleDateFormat(format);
        df.setLenient(false);
        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        Date date;

        try {
            date = df.parse(dateString);
        } catch (ParseException e) {
            throw new RuntimeException(e.getMessage());
        }

        return date;
    }

    public static List<String> timeToList(JSONObject params) {
        List<String> timeList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        //今天开始时间+1 计算周期结束的时间
        String time = params.getString("time");
        if (StringUtils.hasText(time)) {
            if (Time.DAYS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), now.getHour(), 0, 0, 0);
                LocalDateTime endTime = start.minus(1, ChronoUnit.DAYS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusHours(1)) {
                    timeList.add(DateUtil.localDateTimeHourFormat(next));
                }
            }
            if (Time.WEEKS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), now.getHour(), 0, 0, 0);
                LocalDateTime endTime = start.minus(1, ChronoUnit.WEEKS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusHours(6)) {
                    timeList.add(DateUtil.localDateTimeHourFormat(next));
                }
            }
            if (Time.MONTHS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0, 0);
                LocalDateTime endTime = start.minus(1, ChronoUnit.MONTHS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusDays(1)) {
                    timeList.add(DateUtil.localDateFormat(next));
                }
            }
            if (Time.QUARTER_YEARS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0, 0);
                LocalDateTime endTime = start.minus(3, ChronoUnit.MONTHS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusDays(3)) {
                    timeList.add(DateUtil.localDateFormat(next));
                }
            }
            if (Time.HALF_YEARS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0, 0);
                LocalDateTime endTime = start.minus(6, ChronoUnit.MONTHS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusWeeks(1)) {
                    timeList.add(DateUtil.localDateFormat(next));
                }
            }
            if (Time.YEARS.getName().equals(time)) {
                LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0, 0);
                LocalDateTime endTime = start.minus(1, ChronoUnit.YEARS);
                for (LocalDateTime next = start; next.isAfter(endTime) || next.isEqual(endTime); next = next.minusWeeks(2)) {
                    timeList.add(DateUtil.localDateFormat(next));
                }
            }
            Collections.reverse(timeList);
        } else {
            String startTime = params.getString("startTime") != null ? params.getString("startTime") : DateUtil.localDateTimeFormat(now.minusMonths(1));
            String endTime = params.getString("endTime") != null ? params.getString("endTime") : DateUtil.localDateTimeFormat(now);
            timeList = timeToList(startTime, endTime);
        }
        return timeList;
    }

    public static List<String> timeToList(String startTime, String endTime) {
        List<String> timeList = new ArrayList<>();
        LocalDateTime start = LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime end = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Duration between = Duration.between(start, end);
        long hours = between.toHours();
        if (hours >= 24 * 365 * 1.5) {
            //超过一年
            end = LocalDateTime.of(end.getYear(), end.getMonth(), 1, 0, 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusMonths(1)) {
                timeList.add(DateUtil.localMothFormat(next));
            }
        } else if (hours >= 24 * 365) {
            //超过一年
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), 0, 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusWeeks(2)) {
                timeList.add(DateUtil.localDateFormat(next));
            }
        } else if (hours >= 24 * 30 * 6) {
            //超过半年
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), 0, 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusWeeks(1)) {
                timeList.add(DateUtil.localDateFormat(next));
            }
        } else if (hours >= 24 * 30 * 3) {
            //超过三个月
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), 0, 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusDays(3)) {
                timeList.add(DateUtil.localDateFormat(next));
            }
        } else if (hours >= 24 * 30) {
            //超过一个月
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), 0, 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusDays(1)) {
                timeList.add(DateUtil.localDateFormat(next));
            }
        } else if (hours >= 24 * 15) {
            //超过半个月
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), end.getHour(), 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusHours(6)) {
                timeList.add(DateUtil.localDateTimeHourFormat(next));
            }
        } else if(hours >= 24 * 7){
            //超过一周
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), end.getHour(), 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusHours(6)) {
                timeList.add(DateUtil.localDateTimeHourFormat(next));
            }
        }else if (hours >= 24) {
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), end.getHour(), 0, 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusHours(1)) {
                timeList.add(DateUtil.localDateTimeHourFormat(next));
            }
        } else {
            end = LocalDateTime.of(end.getYear(), end.getMonth(), end.getDayOfMonth(), end.getHour(), end.getMinute(), 0, 0);
            for (LocalDateTime next = end; next.isAfter(start) || next.isEqual(start); next = next.minusMinutes(30)) {
                timeList.add(DateUtil.localDateTimeMinFormat(next));
            }
        }
        Collections.reverse(timeList);
        return timeList;
    }

    public enum Time {
        //分钟
        MINUTES("Minutes"),
        //小时
        HOURS("Hours"),
        //天
        DAYS("Days"),
        //周
        WEEKS("Weeks"),
        //
        WEEKDAYS("WeekDays"),
        //月S
        MONTHS("Months"),
        //季度
        QUARTER_YEARS("QuarterYears"),
        //半年
        HALF_YEARS("HalfYears"),
        //年
        YEARS("Years");
        private final String name;
        Time(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}
