package com.hotent.uc.manager.impl;


import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hotent.base.manager.impl.BaseManagerImpl;
import com.hotent.base.util.BeanUtils;
import com.hotent.base.util.JsonUtil;
import com.hotent.base.util.StringUtil;
import com.hotent.base.util.time.DateFormatUtil;
import com.hotent.base.util.time.DateUtil;
import com.hotent.base.util.time.TimeUtil;
import com.hotent.uc.dao.ShiftRuleDao;
import com.hotent.uc.exception.BaseException;
import com.hotent.uc.manager.HolidayTimeManager;
import com.hotent.uc.manager.ShiftRuleManager;
import com.hotent.uc.model.HolidayTime;
import com.hotent.uc.model.ShiftRule;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 
 * <pre> 
 * 描述：轮班规则 处理实现类
 * 构建组：x7
 * 作者:qiuxd
 * 邮箱:qiuxd@jee-soft.cn
 * 日期:2020-08-04 16:07:34
 * 版权：广州宏天软件股份有限公司
 * </pre>
 */
@Service("shiftRuleManager")
public class ShiftRuleManagerImpl extends BaseManagerImpl<ShiftRuleDao, ShiftRule> implements ShiftRuleManager{

    @Resource
    HolidayTimeManager holidayTimeManager;

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

    @Override
    public long computeSendDate(String userId,long minute) throws ParseException, IOException, BaseException {
        long checkDuration = minute*60*1000;
        ShiftRule rule = getRuleByUserId(userId);
        if (BeanUtils.isEmpty(rule))
            return DateUtil.getCurrentTimeInMillis() + checkDuration;

        List<HolidayTime> holidayTimes = getHolidayTimes(rule.getHolidayId());

        Map<Integer,ObjectNode> shiftMap = getMap(rule.getRule());
        //计算审批人排班规则的一周总工作时长
        long totalWorkTime = 0;
        long holidayWorkTime = 0;
        for(int i = 0;i<7;i++){
            ObjectNode dayShift = shiftMap.get(i);
            LocalDateTime shiftStartDateTime = DateFormatUtil.parse("2000-01-01 " + dayShift.get("startTime").asText(),STYLE);
            LocalDateTime shiftEndDateTime = DateFormatUtil.parse("2000-01-01 " + dayShift.get("endTime").asText(),STYLE);
            totalWorkTime += TimeUtil.getTimeMillis(shiftEndDateTime) - TimeUtil.getTimeMillis(shiftStartDateTime);
        }
        //计算审批人补班的工作时长
        if(holidayTimes.size()>0){
            for (HolidayTime holidayTime : holidayTimes) {
                if(holidayTime.getType()==1) {
                    holidayWorkTime += TimeUtil.getTimeMillis(holidayTime.getEndTime()) - TimeUtil.getTimeMillis(holidayTime.getStartTime());
                }
            }
        }
        //如果审批人排班中没有工作时间而且补班的时间小于催办到期时间，无法计算到期时间，流程就无法进行了- -
        if(totalWorkTime == 0 && holidayWorkTime<= checkDuration){
            throw new BaseException("流程进行错误：下个节点审批人的排班中无工作时间，且无补班时间或补班时间小于催办时长");
        }
        LocalDateTime today = DateUtil.getCurrentDate();
        int count = 0;
        long result = TimeUtil.getTimeMillis(today);

        while (checkDuration>0){
            if (count!=0)
                today = today.plusDays(1);
            int day = today.getDayOfWeek().getValue()%7;
            long duration = 0;

            HolidayTime makeUpDay = getMakeUpDay(holidayTimes, TimeUtil.getTimeMillis(today));
            if (!isHoliday(holidayTimes, TimeUtil.getTimeMillis(today)) && isWorkDay(shiftMap.get(day))){
                ObjectNode shift = shiftMap.get(day);
                LocalDateTime startDateTime = DateFormatUtil.parse(DateFormatUtil.format(today, "yyyy-MM-dd ") + shift.get("startTime").asText(),STYLE);
                LocalDateTime endDateTime = DateFormatUtil.parse(DateFormatUtil.format(today, "yyyy-MM-dd ") + shift.get("endTime").asText(),STYLE);
                long startTime = TimeUtil.getTimeMillis(startDateTime);
                long endTime = TimeUtil.getTimeMillis(endDateTime);
                if (endTime - startTime>=checkDuration && count!=0){
                    duration = checkDuration;
                    result = startTime + duration;
                }
                //第一天就计算当前时间到下班时间的时长是否大于时长，如果大于说明催办当天就可以提示不需到第二天
                if (endTime - TimeUtil.getTimeMillis(today) > checkDuration && count == 0) {
                    duration = checkDuration;
                    result = TimeUtil.getTimeMillis(today) + duration;
                }
                if (today.isBefore(startDateTime) || (!today.isBefore(endDateTime)&&today.minusDays(1).isBefore(startDateTime))){
                    duration = endTime - startTime;
                }else if (today.isBefore(endDateTime)){
                    duration = endTime - TimeUtil.getTimeMillis(today);
                    today = LocalDateTime.of(today.toLocalDate(), LocalTime.MIN);
                }
            }else if (BeanUtils.isNotEmpty(makeUpDay)) {
                long startTime = TimeUtil.getTimeMillis(makeUpDay.getStartTime());
                long endTime = TimeUtil.getTimeMillis(makeUpDay.getEndTime());
                if (endTime - startTime >= checkDuration && count!=0) {
                    duration = checkDuration;
                    result = startTime + duration;
                }
                //第一天就计算当前时间到下班时间的时长是否大于时长，如果大于说明催办当天就可以提示不需到第二天
                if (endTime - TimeUtil.getTimeMillis(today) > checkDuration && count == 0) {
                    duration = checkDuration;
                    result = TimeUtil.getTimeMillis(today) + duration;
                }
                if (today.isBefore(makeUpDay.getStartTime()) || (!today.isBefore(makeUpDay.getEndTime())&&today.minusDays(1).isBefore(makeUpDay.getStartTime()))){
                    duration = endTime - startTime;
                }else if (today.isBefore(makeUpDay.getEndTime())){
                    duration = endTime - TimeUtil.getTimeMillis(today);
                    today = LocalDateTime.of(today.toLocalDate(), LocalTime.MIN);
                }
            }
            checkDuration -= duration;
            count++;
        }
        return result;
    }

    @Override
    public long computeDuration(String userId, LocalDateTime startDate, LocalDateTime endDate) throws ParseException, IOException {
        long duration = 0;
        ShiftRule rule = getRuleByUserId(userId);
        if (BeanUtils.isEmpty(rule))
            return TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(startDate);

        List<HolidayTime> holidayTimes = getHolidayTimes(rule.getHolidayId());

        Map<Integer,ObjectNode> shiftMap = getMap(rule.getRule());

        LocalDateTime currentDate = DateFormatUtil.parse(DateFormatUtil.format(startDate, STYLE));

        while(TimeUtil.getTimeMillis(currentDate) < TimeUtil.getTimeMillis(endDate)) {
            int day = currentDate.getDayOfWeek().getValue()%7;
            HolidayTime makeUpDay = getMakeUpDay(holidayTimes, TimeUtil.getTimeMillis(currentDate));
            if (!isHoliday(holidayTimes, TimeUtil.getTimeMillis(currentDate)) && isWorkDay(shiftMap.get(day))){
                ObjectNode shift = shiftMap.get(day);
                LocalDateTime startDateTime = DateFormatUtil.parse(DateFormatUtil.format(currentDate, "yyyy-MM-dd ") + shift.get("startTime").asText(),STYLE);
                LocalDateTime endDateTime = DateFormatUtil.parse(DateFormatUtil.format(currentDate, "yyyy-MM-dd ") + shift.get("endTime").asText(),STYLE);
                if (currentDate.isBefore(startDateTime)) {
                    if (endDate.isBefore(endDateTime)) {
                        duration += TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(startDateTime);
                    }else {
                        duration += TimeUtil.getTimeMillis(endDateTime) - TimeUtil.getTimeMillis(startDateTime);
                    }
                }else if (currentDate.isBefore(endDateTime)) {
                    if (BeanUtils.isNotEmpty(makeUpDay) && endDate.isBefore(makeUpDay.getEndTime())) {
                        duration += TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(currentDate);
                    }else if(endDate.isBefore(endDateTime)){
                        duration += TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(currentDate);
                    }else{
                        duration += TimeUtil.getTimeMillis(endDateTime) - TimeUtil.getTimeMillis(currentDate);
                    }
                }
            }else if (BeanUtils.isNotEmpty(makeUpDay)){
                if (currentDate.isBefore(makeUpDay.getStartTime())) {
                    if (endDate.isBefore(makeUpDay.getEndTime())) {
                        duration += TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(makeUpDay.getStartTime());
                    }else {
                        duration += TimeUtil.getTimeMillis(makeUpDay.getEndTime()) - TimeUtil.getTimeMillis(makeUpDay.getStartTime());
                    }
                    duration += TimeUtil.getTimeMillis(makeUpDay.getEndTime()) - TimeUtil.getTimeMillis(makeUpDay.getStartTime());
                }else if (currentDate.isBefore(makeUpDay.getEndTime())) {
                    if (endDate.isBefore(makeUpDay.getEndTime())) {
                        duration += TimeUtil.getTimeMillis(endDate) - TimeUtil.getTimeMillis(currentDate);
                    }else {
                        duration += TimeUtil.getTimeMillis(makeUpDay.getEndTime()) - TimeUtil.getTimeMillis(currentDate);
                    }
                }
            }
            currentDate = LocalDateTime.of(currentDate.toLocalDate(), LocalTime.MIN);
            currentDate = currentDate.plusDays(1);
        }
        return duration;
    }

    private Map<Integer, ObjectNode> getMap(String rule) throws IOException {
        ArrayNode shifts = (ArrayNode) JsonUtil.toJsonNode(rule);
        List<ObjectNode> shiftList = JsonUtil.arrayToList(shifts);
        Map<Integer,ObjectNode> shiftMap = shiftList.parallelStream().collect(Collectors.toMap(item -> item.get("date").asInt(), item -> item));
        return shiftMap;
    }

    private List<HolidayTime> getHolidayTimes(String holidayId) {
        List<HolidayTime> holidayTimes = new ArrayList<>();
        if (StringUtil.isNotEmpty(holidayId)){
            holidayTimes = holidayTimeManager.getByHolidayId(holidayId);
        }
        return holidayTimes;
    }

    @Override
    public ShiftRule getRuleByUserId(String userId) {
        return baseMapper.getRuleByUserId(userId);
    }

    @Override
    public ShiftRule getByKey(ShiftRule shiftRule) {
        return baseMapper.getByKey(shiftRule.getKey());
    }

    private boolean isWorkDay(ObjectNode shift){
        return shift.get("type").asInt()==1;
    }

    private HolidayTime getMakeUpDay(List<HolidayTime> makeUpDay, long time) {
        List<HolidayTime> times = makeUpDay
                .parallelStream()
                .filter(item -> HolidayTime.MAKE_UP_DAY.equals(item.getType()))
                .filter(item -> {
                    LocalDateTime startDateTime = LocalDateTime.of(item.getStartTime().toLocalDate(), LocalTime.MIN);
                    LocalDateTime endDateTime = LocalDateTime.of(item.getEndTime().toLocalDate(), LocalTime.MAX);
                    return isBetween(startDateTime, endDateTime, time);
                })
                .collect(Collectors.toList());
        if (BeanUtils.isNotEmpty(times)) {
            return times.get(0);
        }
        return null;
    }

    private boolean isHoliday(List<HolidayTime> holidays, long time) {
        return holidays
                .parallelStream()
                .filter(item -> HolidayTime.HOLIDAY.equals(item.getType()))
                .filter(item -> isBetween(item.getStartTime(), item.getEndTime(), time))
                .collect(Collectors.counting()).intValue()>0;
    }

    private boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, long time) {
        if (TimeUtil.getTimeMillis(startTime)<=time && endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()>time)
            return true;
        return false;
    }

}
