package com.engine.jucailinkq.attendance.attendanceanalysis.service.impl;

import com.engine.jucailinkq.attendance.attendanceanalysis.cmd.GetEvectionCmd;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.EvectionService;
import com.engine.jucailinkq.attendance.enums.*;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.Utils;
import com.engine.core.impl.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import weaver.general.Util;

import java.time.ZoneOffset;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EvectionServiceImpl extends Service implements EvectionService {
    @Override
    public Map<String,Object> evectionByTime(Map<String, Object> param) {
        //人员迟到、早退、漏卡等记录
        List<Map<String,Object>> abnormalClockInList = (List<Map<String,Object>>)param.get("abnormalClockInList");
        //请假开始时间
        String kssj = Util.null2String(param.get("kssj"));
        //请假结束时间
        String jssj = Util.null2String(param.get("jssj"));
        //作用时段
        String zysd = Util.null2String(param.get("zysd"));
        //分析日期
        String analysisDate = Util.null2String(param.get("analysisDate"));
        //排班
        List<Map<String,Object>> scheduleResult = (List<Map<String,Object>>)param.get("scheduleResult");

        List<Map<String,Object>> offsetEvectionAnomaly = Lists.newArrayList();
        for (int i=0;i<abnormalClockInList.size();i++){
            String bdlx = Util.null2String(abnormalClockInList.get(i).get("bdlx"));
            String classStartTime = Util.null2String(abnormalClockInList.get(i).get("classStartTime"));
            String classEndTime = Util.null2String(abnormalClockInList.get(i).get("classEndTime"));
            if ((zysd.contains(Utils.getWorkFor(bdlx)) || zysd.contains(WorkForTimeEnum.ALL_TIME.getKey())) &&
                    !"".equals(classStartTime) && !"".equals(classEndTime)) {

                Map<String,Object> leaveMap = Maps.newHashMap();
                leaveMap.put("kssj",kssj);
                leaveMap.put("jssj",jssj);
                int betweenTime = Utils.removeRestTime(classStartTime,classEndTime,scheduleResult,analysisDate);
                int intersectionTime = Utils.getIntersectionTime(classStartTime,classEndTime,leaveMap,scheduleResult,analysisDate);
                if (betweenTime == intersectionTime){
                    offsetEvectionAnomaly.add(abnormalClockInList.get(i));
                }else {
                    String hsdw = Util.null2String(abnormalClockInList.get(i).get("hsdw"));
                    double hsl = Double.valueOf(Util.null2String(abnormalClockInList.get(i).get("hsl")));
                    double itemduration = Utils.getItemduration(hsl,hsdw,betweenTime-intersectionTime, AccountingUnitEnum.MINUTES,Double.valueOf(scheduleResult.get(0).get("edsc").toString()));
                    abnormalClockInList.get(i).put("itemduration",itemduration);
                    abnormalClockInList.get(i).put("betweenMinutes",betweenTime-intersectionTime);
                }
            }
        }
        abnormalClockInList.removeAll(offsetEvectionAnomaly);
        Map<String,Object> resultMap = Maps.newHashMap();
        resultMap.put("abnormalClockInList",abnormalClockInList);
        resultMap.put("offsetEvectionAnomaly",offsetEvectionAnomaly);
        return resultMap;
    }

    @Override
    public Map<String,Object> evectionByDurationTime(Map<String, Object> param) {
        //人员迟到、早退、漏卡等记录
        List<Map<String,Object>> abnormalClockInList = (List<Map<String,Object>>)param.get("abnormalClockInList");
        List<Map<String,Object>> forgetClockList = abnormalClockInList.stream().filter(e -> e.get("betweenMinutes") == null).collect(Collectors.toList());
        abnormalClockInList = abnormalClockInList.stream().filter(e -> e.get("betweenMinutes") != null).collect(Collectors.toList());
        abnormalClockInList = abnormalClockInList.stream().sorted(Comparator.comparing(e->Integer.valueOf(e.get("betweenMinutes").toString()))).collect(Collectors.toList());

        //请假时长,单位小时
        int ccsc = param.get("ccsc") == null?0:Double.valueOf(Math.ceil(Double.valueOf(param.get("ccsc").toString())*60)).intValue();
        //同一天可抵消多个异常
        List<Map<String,Object>> offsetEvectionAnomaly = Lists.newArrayList();
        //作用时段
        String zysd = Util.null2String(param.get("zysd"));

        String tybcndbjlhbjs = Util.null2String(param.get("tybcndbjlhbjs"));
        if (CheckBoxEnum.CHECKED.getKey().equals(tybcndbjlhbjs)){
            for (int i=abnormalClockInList.size() -1;i>=0;i--){
                int time = Integer.valueOf(abnormalClockInList.get(i).get("betweenMinutes").toString());
                String bdlx = Util.null2String(abnormalClockInList.get(i).get("bdlx"));
                if (ccsc > time && (zysd.contains(Utils.getWorkFor(bdlx)) || zysd.contains(WorkForTimeEnum.ALL_TIME.getKey()))){
                    ccsc = ccsc-time;
                    offsetEvectionAnomaly.add(abnormalClockInList.get(i));
                }
            }
            abnormalClockInList.removeAll(offsetEvectionAnomaly);
        }else{
            for (int i=abnormalClockInList.size() -1;i>=0;i--){
                int time = Integer.valueOf(abnormalClockInList.get(i).get("betweenMinutes").toString());
                String bdlx = Util.null2String(abnormalClockInList.get(i).get("bdlx"));
                if (ccsc > time && (zysd.contains(Utils.getWorkFor(bdlx)) || zysd.contains(WorkForTimeEnum.ALL_TIME.getKey()))){
                    offsetEvectionAnomaly.add(abnormalClockInList.get(i));
                    abnormalClockInList.remove(i);
                    break;
                }
            }
        }
        abnormalClockInList.addAll(forgetClockList);
        Map<String,Object> resultMap = Maps.newHashMap();
        resultMap.put("abnormalClockInList",abnormalClockInList);
        resultMap.put("offsetEvectionAnomaly",offsetEvectionAnomaly);
        return resultMap;
    }

    @Override
    public Map<String,Object> evectionByHalfDay(Map<String, Object> param) {
        Map<String,Object> resultMap = Maps.newHashMap();
        //人员迟到、早退、漏卡等记录
        List<Map<String,Object>> abnormalClockInList = (List<Map<String,Object>>)param.get("abnormalClockInList");
        //班次
        List<Map<String,Object>> scheduleResult = (List<Map<String,Object>>)param.get("scheduleResult");
        //作用时段
        String zysd = Util.null2String(param.get("zysd"));
        //作用时段
        String analysisDate = Util.null2String(param.get("analysisDate"));
        //半天规则
        String btgz = Util.null2String(scheduleResult.get(0).get("btgz"));
        //额定时长
        double edsc = Double.valueOf(scheduleResult.get(0).get("edsc").toString());

        Map<String,Object> evectionItem = (Map<String,Object>)param.get("evectionItem");
        //核算量
        double hsl = Double.valueOf(Util.null2String(evectionItem.get("hsl")));
        //核算单位
        String hsdw = Util.null2String(evectionItem.get("hsdw"));

        List<Map<String,Object>> offsetEvectionAnomaly = Lists.newArrayList();

        List<String> classSegmens = Lists.newArrayList();
        for (String str:zysd.split(",")){
            classSegmens.add(Utils.getClassSegmenByWorkFor(str));
        }
        List<Map<String,Object>> abnormalList = Lists.newArrayList();
        for (Map<String,Object> abnormalClock:abnormalClockInList){
            if (classSegmens.contains(abnormalClock.get("bdlx"))){
                abnormalList.add(abnormalClock);
            }
        }

        double itemduration = 0;
        if (btgz.equals(HalfDayRuleREnum.BY_CLASS_SET.getKey())){
            //按班次设置
            Map<String,Object>  haveAbnormalScheduleMap = getHaveAbnormalScheduleMap(scheduleResult,abnormalList,analysisDate);
            List<Map<String,Object>> haveAbnormalScheduleList = (List<Map<String,Object>>)haveAbnormalScheduleMap.get("haveAbnormalScheduleList");
            Map<Map<String, Object>,List<Map<String,Object>>> haveAbnormalscheduleMapGroup = (Map<Map<String, Object>,List<Map<String,Object>>>)haveAbnormalScheduleMap.get("haveAbnormalscheduleMapGroup");

            double restTime = 0.5;
            if (haveAbnormalScheduleList == null || haveAbnormalScheduleList.size() == 0){
                //当没有消除异常时默认额度时长一半、天数固定1天
                if (hsdw.equals(AccountingUnitEnum.HOUR.getKey())){
                    itemduration = edsc*0.5;
                }else if (hsdw.equals(AccountingUnitEnum.DAY.getKey())){
                    itemduration = 0.5;
                }else if (hsdw.equals(AccountingUnitEnum.MINUTES.getKey())){
                    itemduration = edsc*30;
                }else if (hsdw.equals(AccountingUnitEnum.ONCE.getKey())){
                    itemduration = 1;
                }
            }else {
                for (int i=haveAbnormalScheduleList.size()-1;i>=0;i--){
                    double edts = Double.valueOf(haveAbnormalScheduleList.get(i).get("edts").toString());
                    restTime = Utils.subtract(restTime,edts);
                    if (restTime >=0){
                        offsetEvectionAnomaly.addAll(haveAbnormalscheduleMapGroup.get(haveAbnormalScheduleList.get(i)));
                        if (hsdw.equals(AccountingUnitEnum.HOUR.getKey())){
                            itemduration += Double.valueOf(haveAbnormalScheduleList.get(i).get("edxss").toString());
                        }else if (hsdw.equals(AccountingUnitEnum.DAY.getKey())){
                            itemduration = 0.5;
                        }else if (hsdw.equals(AccountingUnitEnum.MINUTES.getKey())){
                            itemduration += Double.valueOf(haveAbnormalScheduleList.get(i).get("edfzs").toString());
                        }else if (hsdw.equals(AccountingUnitEnum.ONCE.getKey())){
                            itemduration = 1;
                        }
                    }
                }
            }

        }else if (btgz.equals(HalfDayRuleREnum.FIXED_DURATION.getKey())){
            //取固定时间
            //分隔时间点
            String fgsjd = Util.null2String(scheduleResult.get(0).get("fgsjd"));
            fgsjd = analysisDate +" "+fgsjd;
            String finalFgsjd = fgsjd;
            List<Map<String,Object>> beforeAbnormalList = abnormalList.stream().filter(e-> DateUtil.getTime(e.get("pointTime").toString()).compareTo(DateUtil.getTime(finalFgsjd)) <=0).collect(Collectors.toList());
            List<Map<String,Object>> afterAbnormalList = abnormalList.stream().filter(e->DateUtil.getTime(e.get("pointTime").toString()).compareTo(DateUtil.getTime(finalFgsjd)) >0).collect(Collectors.toList());
            boolean ifBefore = false;
            if (beforeAbnormalList.size() > afterAbnormalList.size()){
                offsetEvectionAnomaly.addAll(beforeAbnormalList);
                ifBefore=true;
            }else if (beforeAbnormalList.size() < afterAbnormalList.size()){
                offsetEvectionAnomaly.addAll(afterAbnormalList);
            }else {
                Map<String,Object>  beforeHaveAbnormalScheduleMap = getHaveAbnormalScheduleMap(scheduleResult,beforeAbnormalList,analysisDate);
                List<Map<String,Object>> beforehaveAbnormalScheduleList = (List<Map<String,Object>>)beforeHaveAbnormalScheduleMap.get("haveAbnormalScheduleList");
                double beforeTotalAbnormalMinute = beforehaveAbnormalScheduleList.stream().mapToDouble(e->Double.valueOf(e.get("totalAbnormalMinute").toString())).sum();
                Map<String,Object>  afterHaveAbnormalScheduleMap = getHaveAbnormalScheduleMap(scheduleResult,afterAbnormalList,analysisDate);
                List<Map<String,Object>> afterhaveAbnormalScheduleList = (List<Map<String,Object>>)afterHaveAbnormalScheduleMap.get("haveAbnormalScheduleList");
                double afterTotalAbnormalMinute = afterhaveAbnormalScheduleList.stream().mapToDouble(e->Double.valueOf(e.get("totalAbnormalMinute").toString())).sum();
                if (beforeTotalAbnormalMinute > afterTotalAbnormalMinute || beforeTotalAbnormalMinute == afterTotalAbnormalMinute){
                    offsetEvectionAnomaly.addAll(beforeAbnormalList);
                    ifBefore=true;
                }else if (beforeTotalAbnormalMinute < afterTotalAbnormalMinute){
                    offsetEvectionAnomaly.addAll(afterAbnormalList);
                }
            }
            if (hsdw.equals(AccountingUnitEnum.DAY.getKey())){
                itemduration=0.5;
            }else if (hsdw.equals(AccountingUnitEnum.ONCE.getKey())){
                itemduration=1;
            }else {
                String keyName="";
                if (hsdw.equals(AccountingUnitEnum.MINUTES.getKey())){
                    keyName="edfzs";
                }else{
                    keyName="edxss";
                }
                if (ifBefore){
                    List<Map<String,Object>> beforeScheduleList = scheduleResult.stream().filter(e-> DateUtil.getTime(Utils.getkssjTime(e,analysisDate)).compareTo(DateUtil.getTime(finalFgsjd))<=0 && classSegmens.contains(e.get("bdlx"))).collect(Collectors.toList());
                    for (int i=0;i<beforeScheduleList.size();i++){
                        if (i == beforeScheduleList.size()-1){
                            if (keyName.equals("edfzs")){
                                itemduration+=DateUtil.getBetWeenMinutes(Utils.getkssjTime(beforeScheduleList.get(i),analysisDate),fgsjd);
                            }else if (keyName.equals("edxss")){
                                itemduration+=Utils.divide(DateUtil.getBetWeenMinutes(Utils.getkssjTime(beforeScheduleList.get(i),analysisDate),fgsjd),60);
                            }
                        }else {
                            itemduration+=Double.valueOf(beforeScheduleList.get(i).get(keyName).toString());
                        }
                    }
                }else {
                    List<Map<String,Object>> afterScheduleList = scheduleResult.stream().filter(e-> DateUtil.getTime(Utils.getjssjTime(e,analysisDate)).compareTo(DateUtil.getTime(finalFgsjd))>=0 && classSegmens.contains(e.get("bdlx"))).collect(Collectors.toList());
                    for (int i=0;i<afterScheduleList.size();i++){
                        if (i == 0){
                            if (keyName.equals("edfzs")){
                                itemduration+=DateUtil.getBetWeenMinutes(fgsjd,Utils.getjssjTime(afterScheduleList.get(i),analysisDate));
                            }else if (keyName.equals("edxss")){
                                itemduration+=Utils.divide(DateUtil.getBetWeenMinutes(fgsjd,Utils.getjssjTime(afterScheduleList.get(i),analysisDate)),60);
                            }
                        }else {
                            itemduration+=Double.valueOf(afterScheduleList.get(i).get(keyName).toString());
                        }
                    }
                }
            }
        }
        AccountingUnitEnum itemdurationType = null;
        if (hsdw.equals(AccountingUnitEnum.HOUR.getKey())){
            itemdurationType = AccountingUnitEnum.HOUR;
        }else if (hsdw.equals(AccountingUnitEnum.DAY.getKey())){
            itemdurationType = AccountingUnitEnum.DAY;
        }else if (hsdw.equals(AccountingUnitEnum.MINUTES.getKey())){
            itemdurationType = AccountingUnitEnum.MINUTES;
        }else if (hsdw.equals(AccountingUnitEnum.ONCE.getKey())){
            itemdurationType = AccountingUnitEnum.ONCE;
        }
        itemduration = Utils.getItemduration(hsl,hsdw,itemduration,itemdurationType,Double.valueOf(scheduleResult.get(0).get("edsc").toString()));
        abnormalClockInList.removeAll(offsetEvectionAnomaly);
        abnormalClockInList = getAbnormalClockInList(abnormalClockInList,offsetEvectionAnomaly);
        resultMap.put("offsetEvectionAnomaly",offsetEvectionAnomaly);
        resultMap.put("abnormalClockInList",abnormalClockInList);
        resultMap.put("itemduration",itemduration);

        return resultMap;
    }

    @Override
    public Map<String, Object> getEvection(Map<String, Object> param) {
        return commandExecutor.execute(new GetEvectionCmd(param));
    }


    /**
     * 根据班段获得对应的异常
     * @param scheduleResult
     * @param abnormalClockInList
     * @param analysisDate
     * @return
     */
    public Map<String,Object> getHaveAbnormalScheduleMap(List<Map<String,Object>> scheduleResult,List<Map<String,Object>> abnormalClockInList,String analysisDate){
        Map<Map<String, Object>,List<Map<String,Object>>> scheduleMapGroup = Maps.newHashMap();
        List<Map<String,Object>> haveAbnormalScheduleList = Lists.newArrayList();
        for (Map<String, Object> scheduleMap :scheduleResult){
            if (scheduleMap.get("edts") == null || Double.valueOf(scheduleMap.get("edts").toString()) == 0){
                continue;
            }
            List<Map<String,Object>> list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalClockInList,analysisDate);
            if (list.size() > 0){

                double totalTime=0;
                for (Map<String,Object> abnormal:list){
                    AttendanceItemTypeEnum itemType = (AttendanceItemTypeEnum)abnormal.get("itemType");
                    if (itemType == AttendanceItemTypeEnum.MISSE_CARD){
                        totalTime = Double.valueOf(scheduleMap.get("edxss").toString())*60;
                        break;
                    }
                    int betweenMinutes = Integer.valueOf(Util.null2String(abnormal.get("betweenMinutes")==null?"0":abnormal.get("betweenMinutes")));
                    totalTime+=betweenMinutes;
                }
                scheduleMap.put("totalAbnormalMinute",totalTime);
                haveAbnormalScheduleList.add(scheduleMap);
                scheduleMapGroup.put(scheduleMap,list);
            }
        }

        haveAbnormalScheduleList = haveAbnormalScheduleList.stream().sorted(Comparator.comparing(e->Double.valueOf(e.get("totalAbnormalMinute").toString()))).collect(Collectors.toList());
        Map<String,Object> resultMap = Maps.newHashMap();
        resultMap.put("haveAbnormalScheduleList",haveAbnormalScheduleList);
        resultMap.put("haveAbnormalscheduleMapGroup",scheduleMapGroup);
        return resultMap;
    }

    /**
     * 当消除的异常中存在record为true需要记录的异常时，查找符合条件的record为false的异常记录，替代消除的异常
     * @param abnormalClockInList
     * @param offsetEvectionAnomaly
     * @return
     */
    public List<Map<String,Object>> getAbnormalClockInList(List<Map<String,Object>> abnormalClockInList,List<Map<String,Object>> offsetEvectionAnomaly){
        abnormalClockInList = abnormalClockInList.stream().sorted(Comparator.comparing(e->DateUtil.getTime(e.get("pointTime").toString()).toInstant(ZoneOffset.of("+8")).toEpochMilli())).collect(Collectors.toList());
        for (Map<String,Object> offsetAbnormal:offsetEvectionAnomaly){
            if ((boolean)offsetAbnormal.get("record")){
                AttendanceItemTypeEnum itemType = (AttendanceItemTypeEnum)offsetAbnormal.get("itemType");
                String classStartTime = Util.null2String(offsetAbnormal.get("classStartTime"));
                String classEndTime = Util.null2String(offsetAbnormal.get("classEndTime"));
                if (abnormalClockInList.size() > 0){
                    for (int i = abnormalClockInList.size()-1;i>=0;i--){
                        if (!(boolean)abnormalClockInList.get(i).get("record") && abnormalClockInList.get(i).get("itemType") == itemType &&
                                DateUtil.getTime(classStartTime).compareTo(DateUtil.getTime(abnormalClockInList.get(i).get("pointTime").toString())) <= 0 &&
                                DateUtil.getTime(classEndTime).compareTo(DateUtil.getTime(abnormalClockInList.get(i).get("pointTime").toString())) >=0){
                            abnormalClockInList.get(i).put("record",true);
                            break;
                        }
                    }
                }
            }
        }
        return abnormalClockInList;
    }
}
