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

import com.engine.common.util.ServiceUtil;
import com.engine.jucailinkq.attendance.component.persongroup.service.SchedulingResultsService;
import com.engine.jucailinkq.attendance.component.persongroup.service.impl.SchedulingResultsServiceImpl;
import com.engine.jucailinkq.attendance.enums.AccountingUnitEnum;
import com.engine.jucailinkq.attendance.enums.CheckBoxEnum;
import com.engine.jucailinkq.attendance.enums.ClassSegmentTypeEnum;
import com.engine.jucailinkq.attendance.enums.DateTypeEnum;
import com.engine.jucailinkq.attendance.workflow.cmd.GetRestDayIntervalCmd;
import com.engine.jucailinkq.attendance.workflow.enums.AskAndEvctionWayEnum;
import com.engine.jucailinkq.attendance.workflow.service.BusinessTripsApplyService;
import com.engine.jucailinkq.attendance.workflow.service.MakeUpClockInService;
import com.engine.jucailinkq.common.util.CommonUtil;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.DbTools;
import com.engine.core.impl.Service;
import com.engine.jucailinkq.common.util.Utils;
import lombok.extern.slf4j.Slf4j;
import weaver.general.Util;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: sy
 * @Description: 出差申请
 * @Date: 2024/3/15
 **/
@Slf4j
public class BusinessTripsApplyServiceImpl extends Service implements BusinessTripsApplyService {

    private SchedulingResultsService schedulingResultsService = ServiceUtil.getService(SchedulingResultsServiceImpl.class);

    private MakeUpClockInService makeUpClockInService = ServiceUtil.getService(MakeUpClockInServiceImpl.class);

    @Override
    public Map<String, Object> getBusinessTripsApplyList(Map<String, Object> params) {

        Map<String, Object> retmap = new HashMap<String, Object>();
        //获取主表id列表
        String targetUserId = Util.null2String(params.get("targetUserId"));
        String businessTripsType = Util.null2String(params.get("businessTripsType"));
        String startDate = Util.null2String(params.get("startDate"));
        String endDate = Util.null2String(params.get("endDate"));
        log.debug("targetUserId : {}, businessTripsType : {}, startDate : {}, endDate : {}", targetUserId, businessTripsType, startDate, endDate);
        //查询子表数据

        String detailSql = "select a.id,a.cxcc,a.ksrq,a.kssj,a.jsrq,a.jssj,a.ccsc,a.qtcc,a.btcc,a.cxsm from uf_jcl_kq_ccjl_dt1 a left join uf_jcl_kq_ccjl b on a.mainid=b.id where (a.cxcc is null or a.cxcc<>1) and a.ccr =? and a.ksrq >= ? and a.ksrq <= ? and b.cclx=?";
        List<Map<String, Object>> detailList = DbTools.getSqlToList(detailSql,targetUserId,startDate,endDate,businessTripsType);
        if (detailList.size() > 0) {
            retmap.put("result", detailList);
        } else {
            retmap.put("result", null);
        }
        log.debug("detailList : [{}]", detailList);

        return retmap;
    }

    @Override
    public Map<String, Object> generateBusinessTripsList(Map<String, Object> params) {
        Map<String,Object> resultMap = new HashMap<>();
        try {
            List<String> errorMessage = new ArrayList<>();
            // 流程表单主表数据
            Map<String,String> mainTableData = (Map<String,String>)params.get("mainTableData");
            log.info("mainTableData : {}", mainTableData);
            // 流程表单明细表1数据
            List<Map<String, String>> detailTableData = (List<Map<String, String>>)params.get("detailTableData");
            //开始日期、结束日期、出差方式、开始时间、结束时间、出差类型、出差时长
            String startDate = Util.null2String(mainTableData.get("ksrq"));
            String endDate = Util.null2String(mainTableData.get("jsrq"));
            String startTime = Util.null2String(mainTableData.get("kssj"));
            String endTime = Util.null2String(mainTableData.get("jssj"));
            String ccMode = Util.null2String(mainTableData.get("ccsd"));
            String ccType = Util.null2String(mainTableData.get("cclx"));
            String ccDuration = Util.null2String(mainTableData.get("ccsc"));
            String dailyRepeat = Util.null2String(mainTableData.get("mttsdcc"));
            //获取出差日期集合
            List<String> ccDateList = DateUtil.getDatesBetween(startDate, endDate);
            //记录已编辑的出差记录，<人员id-出差类型-日期, 请假时长>
            Map<String, String> editedCcInfo = new HashMap<>();
            //处理明细表1数据，生成触发接口之前编辑内容中的出差信息
            editedCcInfo = dealDetailTableData(detailTableData, editedCcInfo);
            //处理主表数据
            String ccr = Util.null2String(mainTableData.get("ccr"));
            //出差人姓名映射
            Map<String, String> empIdToName = new HashMap<>();
            //出差人员列表
            List<String> ccEmpIdList = new ArrayList<>();
            if (!"".equals(ccr)) {
                ccEmpIdList = Arrays.asList(ccr.split(","));
                String sql = "select id, lastname from hrmresource where id in (" + ccr + ")";
                List<Map<String, Object>> data = DbTools.getSqlToList(sql);
                empIdToName = data.stream().collect(Collectors.toMap(e->Util.null2String(e.get("id")), e->Util.null2String(e.get("lastname"))));
            }
            //获取填写的出差类型关联的考勤项目
            String sql = "select id,mc,hsdw,hsl,jcbyxsyqjb,yxsydjb,qzsyyxjb,zdycbcndfgzsd,zdycrqqjndxxb,yxyz,zdyzsl,zysd from uf_jcl_kq_kqxm where id=?";
            Map<String,Object> holidayItem = DbTools.getSqlToMap(sql,ccType);
            //出差类型名称
            String checkItemName = Util.null2String(holidayItem.get("mc"));
            //核算单位
            String hsdw = Util.null2String(holidayItem.get("hsdw"));
            //核算量
            double hsl = Double.parseDouble(Util.null2String(holidayItem.get("hsl")));
            //核算单位为天时，只允许请全天假、半天假
            if (hsdw.equals(AccountingUnitEnum.DAY.getKey()) && !ccMode.equals(AskAndEvctionWayEnum.ALLDAY.getKey()) && !ccMode.equals(AskAndEvctionWayEnum.HALFDAY.getKey())) {
                resultMap.put("status", false);
                resultMap.put("errorInfo", "出差类型的核算单位为天时，只允许选择全天、半天！");
                resultMap.put("data", null);
                return resultMap;
            }
            //判断是否需要自动移除时间区间内的非工作时长、自动移除日期区间内的休息日
            boolean removeNonWorkTimeRange = "1".equals(Util.null2String(holidayItem.get("zdycbcndfgzsd")));
            boolean removeNonWorkDayRange = "1".equals(Util.null2String(holidayItem.get("zdycrqqjndxxb")));
            //获取作用时段
            String zysd = Util.null2String(holidayItem.get("zysd"));
            //获取需要统计时长的班段类型集合（仅作用在“指定时间区间”请假方式）
            List<String> countBdlxList = new ArrayList<>();
            List<String> zysdList = new ArrayList<>();
            if (!"".equals(zysd)) {
                zysdList = Arrays.asList(zysd.split(","));
                for(String zysdKey : zysdList) {
                    countBdlxList.add(Utils.getClassSegmenByWorkFor(zysdKey));
                }
                if (!removeNonWorkTimeRange) {
                    countBdlxList.add(ClassSegmentTypeEnum.REST_AND_DINE.getKey());
                    countBdlxList.add(ClassSegmentTypeEnum.REST_PERIOD.getKey());
                    countBdlxList.add(ClassSegmentTypeEnum.DINING_PERIOD.getKey());
                }
            }
            //遍历人员、日期，生成人员+日期+出差时长的出差信息
            //出差人的出差区间内每一天的日期类型信息，来自于企业日历
            Map<String, List<String>> restDayInfo = removeNonWorkDayRange ? getRestDayWithEmpId(ccEmpIdList, startDate, endDate) : new HashMap<>();
            //出差人的出差区间内的排班结果
            Map<String, List<Map<String, Object>>> scheduleInfoMap = getScheduleInfoWithEmpId(ccEmpIdList, DateUtil.beforeDay(startDate,1), DateUtil.AfterDay(endDate,1));

            //生成出差明细数据
            List<Map<String, String>> simpleDetailList;
            if (ccMode.equals(AskAndEvctionWayEnum.TIME_INTERVAL.getKey()) && !"1".equals(dailyRepeat)) {
                simpleDetailList = createDetailListWithNoDaily(ccEmpIdList, ccDateList, removeNonWorkDayRange, scheduleInfoMap,
                        restDayInfo, startDate, endDate, startTime, endTime, countBdlxList, hsdw, hsl, ccType, empIdToName, checkItemName);
            } else {
                simpleDetailList = createDetailList(ccEmpIdList, ccDateList, removeNonWorkDayRange, scheduleInfoMap,
                        restDayInfo, ccMode, startTime, endTime, ccDuration, countBdlxList, hsdw, hsl, ccType, empIdToName, checkItemName);
            }

            //按照人员id分组处理请假明细信息，关联假期余额数据
            Map<String, List<Map<String, String>>> ccDetailGroupByEmp = simpleDetailList.stream().collect(Collectors.groupingBy(e -> Util.null2String(e.get("ccr"))));
            //收集需要新建的出差明细数据
            List<Map<String, String>> completeLeaveDetailList = new ArrayList<>();
            Map<String, String> finalEditedCcInfo = editedCcInfo;
            for(Map.Entry<String,List<Map<String,String>>> entry : ccDetailGroupByEmp.entrySet()) {
                //校验该人员的考勤周期是否正常
                boolean kqCycleAllow = kqCycleCheck(entry.getKey(), entry.getValue(), errorMessage, empIdToName.get(entry.getKey()));
                if (!kqCycleAllow) {
                    continue;
                }
                List<Map<String,String>> detailListItem = entry.getValue().stream()
                        .filter(f -> finalEditedCcInfo.get(Util.null2String(f.get("ccr")) + "_" + Util.null2String(f.get("cclx")) + "_" + Util.null2String(f.get("ksrq"))) == null)
                        .collect(Collectors.toList());
                if (detailListItem.size() > 0) {
                    completeLeaveDetailList.addAll(detailListItem);
                }
            }

            if (errorMessage.size() == 0) {
                resultMap.put("status", true);
                resultMap.put("data", completeLeaveDetailList);
            } else {
                resultMap.put("status", false);
                resultMap.put("errorInfo", errorMessage);
                resultMap.put("data", null);
            }

        } catch (Exception e) {
            log.info(e.getMessage());
            resultMap.put("status", false);
            resultMap.put("errorInfo", e.getMessage());
            resultMap.put("data", null);
        }
        return resultMap;
    }

    /**
     * 出差人的出差区间内属于休息日的日期集合，企业日历角度
     * @param empIdList 出差人id集合
     * @param startDate 开始日期
     * @param endDate 结束日期
     */
    private Map<String, List<String>> getRestDayWithEmpId(List<String> empIdList, String startDate, String endDate) {
        Map<String, List<String>> restDayInfoWithEmpId = new HashMap<>();

        Map<String, Object> restDayParam = new HashMap<>();
        restDayParam.put("startDate", startDate);
        restDayParam.put("endDate", endDate);

        List<String> dateTypeList = new ArrayList<>();
        dateTypeList.add(DateTypeEnum.HOLIDAY.getKey());
        dateTypeList.add(DateTypeEnum.PUBLIC_RESTDAY.getKey());
        dateTypeList.add(DateTypeEnum.EXCHANGE_LEAVEDAY.getKey());
        Map<String, Object> restDayInfo;
        List<Map<String,Object>> dataList;
        List<String> restDateList = new ArrayList<>();
        for (String empId : empIdList) {
            restDayParam.put("userId", empId);
            restDayInfo = commandExecutor.execute(new GetRestDayIntervalCmd(restDayParam,user));
            dataList = (List<Map<String, Object>>) restDayInfo.get("data");
            if (dataList != null && dataList.size() > 0) {
                restDateList = dataList.stream()
                        .filter(f -> dateTypeList.contains(Util.null2String(f.get("rqlx"))))
                        .map(e -> Util.null2String(e.get("rq")))
                        .collect(Collectors.toList());
            }
            restDayInfoWithEmpId.put(empId, restDateList);
        }
        return restDayInfoWithEmpId;
    }

    /**
     * 出差人的出差区间内的排班结果
     * @param empIdList 出差人id集合
     * @param startDate 开始日期
     * @param endDate 结束日期
     */
    private Map<String, List<Map<String, Object>>> getScheduleInfoWithEmpId(List<String> empIdList, String startDate, String endDate) {
        Map<String, List<Map<String, Object>>> scheduleInfo = new HashMap<>();
        Map<String,Object> params = new HashMap<>();
        params.put("tableName", "uf_pbjg");
        params.put("startDate", startDate);
        params.put("endDate", endDate);
        params.put("pblx", "0");
        params.put("current", "1");
        params.put("pageSize", "999");
        params.put("recurrence", "1");
        for (String empId : empIdList) {
            params.put("pbdx", empId);
            Map<String,Object> schedulingResultsMap = schedulingResultsService.queryDataTableActualUse(params);
            scheduleInfo.put(empId, (List<Map<String, Object>>) schedulingResultsMap.get("data"));
        }
        return scheduleInfo;
    }

    /**
     * @param empId 人员id
     * @param detailTableData 明细数据列表
     * @param errorMessage 错误信息
     * @param empName 人员姓名
     * @return 校验该人员的考勤周期是否正常
     */
    private boolean kqCycleCheck(String empId, List<Map<String, String>> detailTableData, List<String> errorMessage, String empName) {

        Map<String, Object> params = new HashMap<>();
        params.put("userId",empId);
        params.put("submitDate",DateUtil.getCurrentDate());
        params.put("submitStr","ksrq");
        params.put("submitDataList",detailTableData);
        if (detailTableData == null || detailTableData.size() == 0){
            errorMessage.add(empName + "没有明细数据！");
            return false;
        }
        Map<String,Object> dataMap = makeUpClockInService.getKqCycleTimeIntervalCmd(params);
        List<Map<String,Object>> closeList = (List<Map<String,Object>>)dataMap.get("closeList");
        List<String> nocycleList = (List<String>)dataMap.get("nocycleList");

        boolean status = (boolean)dataMap.get("status");
        if (!status){
            errorMessage.add(empName + "没有考勤周期！");
            return false;
        }
        if (closeList.size() > 0 || nocycleList.size() > 0){
            String message = empName + "：";
            if (nocycleList.size() > 0){
                message = message +String.join(",",nocycleList)+"未找对对应的考勤周期；";
            }
            if (closeList.size() > 0){
                List<String> list = closeList.stream().map(e->e.get("rq").toString()).collect(Collectors.toList());
                message = message +String.join(",",list)+"对应的考勤周期的考勤周期已关账";
            }
            errorMessage.add(message);
            return false;
        }
        return true;
    }

    /**
     * @param detailTableData 明细表1数据
     * @param editedLeaveInfo 记录已编辑的出差记录，<人员id-出差类型-日期, 出差时长>
     * @return 处理明细表1数据，生成触发接口之前编辑内容中的出差信息
     */
    private Map<String, String> dealDetailTableData(List<Map<String, String>> detailTableData, Map<String, String> editedLeaveInfo) {
        if (detailTableData != null && detailTableData.size() > 0){
            editedLeaveInfo = detailTableData.stream()
                    .collect(Collectors.toMap(e-> Util.null2String(e.get("ccr")) + "_" + Util.null2String(e.get("cclx")) + "_" + Util.null2String(e.get("ksrq")),
                            e->Util.null2String(e.get("qjsc"))));
        }
        return editedLeaveInfo;
    }

    /**
     * @param empIdList 人员id集合
     * @param dateList 日期集合
     * @param removeNonWorkDayRange 是否移除非工作日时长
     * @param scheduleInfoMap 排班信息
     * @param restDayInfo 休息日信息
     * @param mode 出差方式
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param ccDuration 出差时长
     * @param countBdlxList 收集需要统计时长的班段类型
     * @return 组装初步的出差明细
     */
    private List<Map<String, String>> createDetailList(List<String> empIdList, List<String> dateList, boolean removeNonWorkDayRange,
                                                                  Map<String, List<Map<String, Object>>> scheduleInfoMap, Map<String, List<String>> restDayInfo,
                                                                  String mode, String startTime, String endTime, String ccDuration, List<String> countBdlxList,
                                                                  String hsdw, double hsl, String ccType, Map<String, String> empIdToName, String checkItemName) {
        List<Map<String, String>> simpleDetailList = new ArrayList<>();
        Map<String, String> simpleDetailItem;
        for (String ccEmpId : empIdList) {
            //需要自动移除日期区间内的休息日时，去除出差日期区间中的休息日
            List<Map<String, Object>> scheduleInfoList = scheduleInfoMap.getOrDefault(ccEmpId, new ArrayList<>());
            Map<String, String> dateToBcxxMap = scheduleInfoList.stream().collect(Collectors.toMap(e->Util.null2String(e.get("bcrq")),e->Util.null2String(e.get("bcxx"))));
            List<String> restDateList = new ArrayList<>();
            if (removeNonWorkDayRange) {
                //排班结果中休息的日期
                List<String> restDateListFromSchedule = scheduleInfoList.stream()
                        .filter(f -> Util.null2String(f.get("sfxx")).equals(CheckBoxEnum.CHECKED.getKey()))
                        .map(e -> Util.null2String(e.get("bcrq")))
                        .collect(Collectors.toList());
                List<String> allDateListFromSchedule = scheduleInfoList.stream().map(e -> Util.null2String(e.get("bcrq"))).collect(Collectors.toList());
                //排班结果中未出现的日期
                List<String> nonSetDateListFromSchedule = dateList.stream().filter(f -> !allDateListFromSchedule.contains(f)).collect(Collectors.toList());
                //企业日历中人员对应日期的休息日
                restDateList = restDayInfo.get(ccEmpId);
                //筛选排班信息无法辨别的日期，依靠企业日历的日期信息
                restDateList = restDateList.stream().filter(nonSetDateListFromSchedule::contains).collect(Collectors.toList());
                restDateList.addAll(restDateListFromSchedule);
            }
            String ccEmpName = empIdToName.get(ccEmpId);
            for (String ccDate : dateList) {
                //出差方式为“指定时间区间”时
                if (mode.equals(AskAndEvctionWayEnum.TIME_INTERVAL.getKey())) {
                    simpleDetailItem = new HashMap<>();
                    //组装初步的明细数据
                    simpleDetailItem.put("ccr", ccEmpId);
                    simpleDetailItem.put("cclx", ccType);
                    simpleDetailItem.put("ccrName", ccEmpName);
                    simpleDetailItem.put("cclxName", checkItemName);
                    simpleDetailItem.put("ksrq", ccDate);
                    simpleDetailItem.put("jsrq", ccDate);
                    simpleDetailItem.put("kssj", startTime);
                    simpleDetailItem.put("jssj", endTime);
                    //获取当天班次id
                    String currentDayBcId = restDateList.contains(ccDate) ? "" : Util.null2String(dateToBcxxMap.get(ccDate)).split("-")[0];
                    //获取前一天班次id
                    String yesterday = DateUtil.beforeDay(ccDate,1);
                    String yesterdayBcId = restDateList.contains(yesterday) ? "" : Util.null2String(dateToBcxxMap.get(yesterday)).split("-")[0];
                    //获取次日班次id
                    String nextDay = DateUtil.AfterDay(ccDate,1);
                    String nextDayBcId = restDateList.contains(nextDay) ? "" : Util.null2String(dateToBcxxMap.get(nextDay)).split("-")[0];
                    String sql = "";
                    List<Map<String, Object>> bcDetailData;
                    String endDate = ccDate;
                    int scMinutes = 0;
                    //开始时时和结束时间存在跨天情况时,即开始时间大于等于结束时间
                    if (startTime.compareTo(endTime) >= 0) {
                        endDate = nextDay;
                        simpleDetailItem.put("jsrq", nextDay);
                    }
                    if (!"".equals(yesterdayBcId)) {
                        sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + yesterdayBcId;
                        bcDetailData = DbTools.getSqlToList(sql);
                        bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
                        //获取需要累计的班段时长区间和请假区间存在交集的分钟数
                        scMinutes = Utils.removeTime(ccDate + " " + startTime, endDate + " " + endTime, bcDetailData, yesterday);
                    }
                    if (!"".equals(currentDayBcId)) {
                        sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + currentDayBcId;
                        bcDetailData = DbTools.getSqlToList(sql);
                        bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
                        //获取需要累计的班段时长区间和请假区间存在交集的分钟数
                        scMinutes = scMinutes + Utils.removeTime(ccDate + " " + startTime, endDate + " " + endTime, bcDetailData, ccDate);
                    }
                    if (!"".equals(nextDayBcId)) {
                        sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + nextDayBcId;
                        bcDetailData = DbTools.getSqlToList(sql);
                        bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
                        //获取需要累计的班段时长区间和请假区间存在交集的分钟数
                        scMinutes = scMinutes + Utils.removeTime(ccDate + " " + startTime, endDate + " " + endTime, bcDetailData, nextDay);
                    }
                    //增加加班计划中的数据，加班计划明细中的数据作为“加班计划”班段类型数据的补充
                    if (countBdlxList.contains(ClassSegmentTypeEnum.OVERTIME_PLAN.getKey())) {
                        sql = "select b.jbry,b.ksrq,b.kssj,b.jblx,b.jsrq,b.jssj,b.jbsc,b.gsrq from uf_jcl_kq_jbjh a left join uf_jcl_kq_jbjh_dt1 b on a.id=b.mainid where b.jbry =? and b.ksrq>=? and b.jsrq<=? and (b.jbcx=0 or b.jbcx is null) and a.jlzt=1";
                        List<Map<String,Object>> overtimePlanList = DbTools.getSqlToList(sql, ccEmpId, DateUtil.beforeDay(ccDate,1), DateUtil.AfterDay(endDate,1));
                        int scMinutesInOvertimePlan = Utils.removeTimeWithOvertimePlan(ccDate + " " + startTime, endDate + " " + endTime, overtimePlanList);
                        scMinutes = scMinutes + scMinutesInOvertimePlan;
                    }
                    if (scMinutes > 0) {
                        double ccscByAccount = Utils.getItemduration(hsl, hsdw, scMinutes, AccountingUnitEnum.MINUTES, 8.0);
                        //组装初步的出差时长
                        simpleDetailItem.put("ccsc", hsdw.equals(AccountingUnitEnum.MINUTES.getKey())
                                ? String.format("%.2f", ccscByAccount / 60.0)
                                : String.valueOf(ccscByAccount));
                        simpleDetailList.add(simpleDetailItem);
                    }
                } else if (mode.equals(AskAndEvctionWayEnum.HOUR.getKey()) && !restDateList.contains(ccDate)) {
                    simpleDetailItem = new HashMap<>();
                    //组装初步的明细数据
                    simpleDetailItem.put("ccr", ccEmpId);
                    simpleDetailItem.put("cclx", ccType);
                    simpleDetailItem.put("ccrName", ccEmpName);
                    simpleDetailItem.put("cclxName", checkItemName);
                    simpleDetailItem.put("ksrq", ccDate);
                    simpleDetailItem.put("jsrq", ccDate);
                    simpleDetailItem.put("ccsc", ccDuration);
                    if (Double.parseDouble(ccDuration) > 0) {
                        double ccscByAccount = Utils.getItemduration(hsl, hsdw, Double.parseDouble(ccDuration), AccountingUnitEnum.HOUR, 8.0);
                        //组装初步的出差时长
                        simpleDetailItem.put("ccsc", hsdw.equals(AccountingUnitEnum.MINUTES.getKey())
                                ? String.format("%.2f", ccscByAccount / 60.0)
                                : String.valueOf(ccscByAccount));
                        simpleDetailList.add(simpleDetailItem);
                    }
                } else if (mode.equals(AskAndEvctionWayEnum.ALLDAY.getKey()) && !restDateList.contains(ccDate)) {
                    simpleDetailItem = new HashMap<>();
                    //组装初步的明细数据
                    simpleDetailItem.put("ccr", ccEmpId);
                    simpleDetailItem.put("cclx", ccType);
                    simpleDetailItem.put("ccrName", ccEmpName);
                    simpleDetailItem.put("cclxName", checkItemName);
                    simpleDetailItem.put("ksrq", ccDate);
                    simpleDetailItem.put("jsrq", ccDate);
                    simpleDetailItem.put("qtcc", "1");
                    simpleDetailList.add(simpleDetailItem);
                } else if (mode.equals(AskAndEvctionWayEnum.HALFDAY.getKey()) && !restDateList.contains(ccDate)) {
                    simpleDetailItem = new HashMap<>();
                    //组装初步的明细数据
                    simpleDetailItem.put("ccr", ccEmpId);
                    simpleDetailItem.put("cclx", ccType);
                    simpleDetailItem.put("ccrName", ccEmpName);
                    simpleDetailItem.put("cclxName", checkItemName);
                    simpleDetailItem.put("ksrq", ccDate);
                    simpleDetailItem.put("jsrq", ccDate);
                    simpleDetailItem.put("btcc", "1");
                    simpleDetailList.add(simpleDetailItem);
                }
            }
        }
        return simpleDetailList;
    }

    /**
     * @param empIdList 出差人员id集合
     * @param dateList 出差日期集合
     * @param removeNonWorkDayRange 是否移除非工作日时长
     * @param scheduleInfoMap 排班信息
     * @param restDayInfo 休息日信息
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param countBdlxList 收集需要统计时长的班段类型
     * @return 组装初步的出差明细，这边只处理“每天同时段出差”未勾选，且出差方式为“指定时间区间”
     */
    private List<Map<String, String>> createDetailListWithNoDaily(List<String> empIdList, List<String> dateList, boolean removeNonWorkDayRange,
                                                                             Map<String, List<Map<String, Object>>> scheduleInfoMap, Map<String, List<String>> restDayInfo,
                                                                             String startDate, String endDate, String startTime, String endTime, List<String> countBdlxList,
                                                                             String hsdw, double hsl, String ccType, Map<String, String> empIdToName, String checkItemName) {
        List<Map<String, String>> simpleDetailList = new ArrayList<>();
        Map<String, String> simpleDetailItem;
        String sql = "";
        List<Map<String, Object>> bcDetailData;
        String targetDateBcId = "";
        dateList.add(0, DateUtil.beforeDay(startDate,1));
        dateList.add(DateUtil.AfterDay(endDate,1));
        for (String ccEmpId : empIdList) {
            int scMinutes = 0;
            //需要自动移除日期区间内的休息日时，去除出差日期区间中的休息日
            List<Map<String, Object>> scheduleInfoList = scheduleInfoMap.getOrDefault(ccEmpId, new ArrayList<>());
            Map<String, String> dateToBcxxMap = scheduleInfoList.stream().collect(Collectors.toMap(e->Util.null2String(e.get("bcrq")),e->Util.null2String(e.get("bcxx"))));

            List<String> restDateList = new ArrayList<>();
            if (removeNonWorkDayRange) {
                //排班结果中休息的日期
                List<String> restDateListFromSchedule = scheduleInfoList.stream()
                        .filter(f -> Util.null2String(f.get("sfxx")).equals(CheckBoxEnum.CHECKED.getKey()))
                        .map(e -> Util.null2String(e.get("bcrq")))
                        .collect(Collectors.toList());
                List<String> allDateListFromSchedule = scheduleInfoList.stream().map(e -> Util.null2String(e.get("bcrq"))).collect(Collectors.toList());
                //排班结果中未出现的日期
                List<String> nonSetDateListFromSchedule = dateList.stream().filter(f -> !allDateListFromSchedule.contains(f)).collect(Collectors.toList());
                //企业日历中人员对应日期的休息日
                restDateList = restDayInfo.get(ccEmpId);
                //筛选排班信息无法辨别的日期，依靠企业日历的日期信息
                restDateList = restDateList.stream().filter(nonSetDateListFromSchedule::contains).collect(Collectors.toList());
                restDateList.addAll(restDateListFromSchedule);
            }
            String ccEmpName = empIdToName.get(ccEmpId);
            simpleDetailItem = new HashMap<>();
            //组装初步的出差明细数据
            simpleDetailItem.put("ccr", ccEmpId);
            simpleDetailItem.put("cclx", ccType);
            simpleDetailItem.put("ccrName", ccEmpName);
            simpleDetailItem.put("cclxName", checkItemName);
            simpleDetailItem.put("ksrq", startDate);
            simpleDetailItem.put("jsrq", endDate);
            simpleDetailItem.put("kssj", startTime);
            simpleDetailItem.put("jssj", endTime);
            for (String date : dateList) {
                targetDateBcId = restDateList.contains(date) ? "" : Util.null2String(dateToBcxxMap.get(date)).split("-")[0];
                if (!"".equals(targetDateBcId)) {
                    sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + targetDateBcId;
                    bcDetailData = DbTools.getSqlToList(sql);
                    bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
                    //获取需要累计的班段时长区间和请假区间存在交集的分钟数
                    scMinutes = scMinutes + Utils.removeTime(startDate + " " + startTime, endDate + " " + endTime, bcDetailData, date);
                }
            }
            //增加加班计划中的数据，加班计划明细中的数据作为“加班计划”班段类型数据的补充
            if (countBdlxList.contains(ClassSegmentTypeEnum.OVERTIME_PLAN.getKey())) {
                sql = "select b.jbry,b.ksrq,b.kssj,b.jblx,b.jsrq,b.jssj,b.jbsc,b.gsrq from uf_jcl_kq_jbjh a left join uf_jcl_kq_jbjh_dt1 b on a.id=b.mainid where b.jbry =? and b.ksrq>=? and b.jsrq<=? and (b.jbcx=0 or b.jbcx is null) and a.jlzt=1";
                List<Map<String,Object>> overtimePlanList = DbTools.getSqlToList(sql, ccEmpId, DateUtil.beforeDay(startDate,1), DateUtil.AfterDay(endDate,1));
                int scMinutesInOvertimePlan = Utils.removeTimeWithOvertimePlan(startDate + " " + startTime, endDate + " " + endTime, overtimePlanList);
                scMinutes = scMinutes + scMinutesInOvertimePlan;
            }
            if (scMinutes > 0) {
                double ccscByAccount = Utils.getItemduration(hsl, hsdw, scMinutes, AccountingUnitEnum.MINUTES, 8.0);
                //组装初步的出差时长
                simpleDetailItem.put("ccsc", hsdw.equals(AccountingUnitEnum.MINUTES.getKey())
                        ? String.format("%.2f", ccscByAccount / 60.0)
                        : String.valueOf(ccscByAccount));
                simpleDetailList.add(simpleDetailItem);
            }
        }
        return simpleDetailList;
    }
}
