package com.yonyou.hrcloud.leave.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yonyou.hrcloud.adder.context.AppContext;
import com.yonyou.hrcloud.adder.exception.AdderBusinessException;
import com.yonyou.hrcloud.adder.model.param.Sorter;
import com.yonyou.hrcloud.adder.model.query.Condition;
import com.yonyou.hrcloud.adder.service.IGenericService;
import com.yonyou.hrcloud.adder.utils.data.TypeConvert;
import com.yonyou.hrcloud.attend.model.AttendPeriod;
import com.yonyou.hrcloud.attend.model.AttendSchemeVO;
import com.yonyou.hrcloud.attend.model.AttendStaff;
import com.yonyou.hrcloud.attend.service.IAttendPeriodService;
import com.yonyou.hrcloud.attend.service.IAttendSchemeService;
import com.yonyou.hrcloud.attentrpt.dao.IAttendRptCalDao;
import com.yonyou.hrcloud.berry.helper.DateUtil;
import com.yonyou.hrcloud.businesstrip.helper.convertor.TsBusinessVariableConvertor;
import com.yonyou.hrcloud.calendar.helper.CalendarCommon;
import com.yonyou.hrcloud.formula.helper.FormulaUtils;
import com.yonyou.hrcloud.leave.dao.ILeaveBalanceRptDao;
import com.yonyou.hrcloud.leave.helper.ITsFormulaConstant;
import com.yonyou.hrcloud.leave.helper.LeaveCalHelper;
import com.yonyou.hrcloud.leave.helper.convertor.TsLeaveVariableConvertor;
import com.yonyou.hrcloud.leave.model.CalValueByState;
import com.yonyou.hrcloud.leave.model.LeaveApply;
import com.yonyou.hrcloud.leave.model.LeaveBalanceRpt;
import com.yonyou.hrcloud.leave.model.LeaveBalanceRptCalParams;
import com.yonyou.hrcloud.leave.model.LeaveTypeNew;
import com.yonyou.hrcloud.leave.service.ILeaveBlanceRptCalService;
import com.yonyou.hrcloud.leave.service.ILeaveCalHelpService;
import com.yonyou.hrcloud.leave.service.ILeaveCommonService;
import com.yonyou.hrcloud.leave.service.ILeaveTypeNewService;
import com.yonyou.hrcloud.overtime.model.OvertimeSplitBillVO;
import com.yonyou.hrcloud.tcommon.helper.HttpClientUtils;
import com.yonyou.hrcloud.tcommon.helper.convertor.TsAttendTypeConvertor;
import com.yonyou.hrcloud.tcommon.helper.convertor.TsFormulaAttendStaffConvertor;
import com.yonyou.hrcloud.tcommon.model.MQParamsDTO;
import com.yonyou.hrcloud.tcommon.model.TaskLogVO;
import com.yonyou.hrcloud.tcommon.model.TimeConst;
import com.yonyou.hrcloud.tcommon.model.TsAttendRptBasicCalParamVO;
import com.yonyou.hrcloud.tcommon.utils.AttendCommonUtils;
import com.yonyou.iuap.utils.PropertyUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by wenbinc on 2017/12/28.
 */
@Slf4j
@Service
@Transactional
@Scope("prototype")
public class LeaveBlanceRptCalImpl extends LeaveRptBasicCalServiceImpl implements ILeaveBlanceRptCalService {

    @Autowired
    private ILeaveBalanceRptDao dao;
    @Autowired
    private ILeaveTypeNewService leaveTypeNewService;
    @Autowired
    private IGenericService genericService;
    @Autowired
    private ILeaveCommonService leaveCommonService;
    @Autowired
    private ILeaveCalHelpService leaveCalHelpService;
    @Resource
    private IAttendSchemeService iAttendSchemeService;
    @Resource
    private IAttendPeriodService periodService;

    @Resource
    private IAttendRptCalDao attendRptCalDao;
    
    @Override
    public void setFormulaFlag(String flag) {

    }
    @Override
    public List<Class> getExtConvertors() {
        List<Class> list = new ArrayList<>();
        list.add(TsFormulaAttendStaffConvertor.class);
        list.add(TsLeaveVariableConvertor.class);
        list.add(TsBusinessVariableConvertor.class);
        list.add(TsAttendTypeConvertor.class);
        super.setExtConvertors(list);
        return list;
    }
    /***
     * 定时任务计算
     * 休假额度定时任务规则：
     * 执行时间：每月月底最后一天22:00，执行计算次月的额度。如果是按考勤年，仅在12月底执行的时候计算次年的额度，其余月份不计算
     * 计算休假类型：当前租户下所有考勤规则启用休假额度的假别
     * 计算人员范围：休假类型-休假规则-假勤方案-适用人员
     * 入职周年问题：假勤档案存储入职日期字段（前端不展示），遍历员工入职日期，对在本月的用户计算次年的额度
     * @param  mqParamsDTO mqParamsDTO
     */
    @Override
    public void calculateRptForMQ(MQParamsDTO mqParamsDTO, boolean isTimeTask){
        int psnNum = 0;
        String logTitle = getLogTitle(mqParamsDTO);
        // 定时任务设置上下文信息
        AppContext.getContext().setScope(mqParamsDTO.getTenantId());
        AppContext.getContext().setUserId(mqParamsDTO.getUserId());
        if(!AttendCommonUtils.isNCC()){
            AppContext.getContext().setSysId("diwork");
        }
        try {
            //查询假勤方案
            Condition condition = new Condition();
            Sorter sorter = new Sorter(Sort.Direction.DESC, "isdefault");
            List<AttendSchemeVO> attendSchemeVOList = iAttendSchemeService.queryByConditionNoPower(condition, sorter);
            if (CollectionUtils.isEmpty(attendSchemeVOList)) {
                callback(1, mqParamsDTO.getLogId(), logTitle, "任务执行完成(未获取到假勤方案)");
                return;
            }
            log.error(String.format("%s，共%s个假勤方案", logTitle, attendSchemeVOList.size()));
            String year = null;
            String month = null;
            // 调用获取假勤方案和组织之间关系的接口
            List<String> schemeIdList = FormulaUtils.getPropertyListFromBeanVO(attendSchemeVOList, "id");
            Map<String,List<String>> schemeOrgMap = iAttendSchemeService.querySchemeAndOrgsMap(schemeIdList);
            for (AttendSchemeVO attendSchemeVO : attendSchemeVOList) {
                try {
                    log.error(String.format("%s，开始处理假勤方案(%s)", logTitle, attendSchemeVO.getName()));
                    // 从map中获取假勤方案对应的组织
                    if(schemeOrgMap != null && schemeOrgMap.size() > 0){
                        attendSchemeVO.setOrgs(schemeOrgMap.get(attendSchemeVO.getId()));
                    }
                    if (CollectionUtils.isEmpty(attendSchemeVO.getOrgs())) {
                        log.debug(String.format("%s，假勤方案(%s),适用范围为空，不计算", logTitle, attendSchemeVO.getName()));
                        continue;
                    }
                    AttendPeriod period = periodService.queryPeriodByDate(new Date(),attendSchemeVO.getPeriodSchemeId());
                    log.debug("考勤期间period::"+JSON.toJSONString(period));
                    if (null == period) {
                        log.debug(String.format("%s，假勤方案(%s),对应考勤周期为空，不计算", logTitle, attendSchemeVO.getName()));
                        continue;
                    }
                    List<LeaveTypeNew> leaveTypeNews = leaveTypeNewService.queryByIsLimit(attendSchemeVO.getLeaveRuleId(),"1");
                    for(LeaveTypeNew ltn : leaveTypeNews) {
                        Map<String, Object> resultMap = checkToCal(ltn, period, attendSchemeVO,false,isTimeTask);
                        String isContinue = String.valueOf(resultMap.get("isContinue"));
                        List<String> staffIdSelect = null;
                        if("true".equals(isContinue)){
                            continue;
                        }else{
                            if(String.valueOf(TimeConst.CALCULATE_TYPE_YEAR).equals(ltn.getPeriodtype())){
                                year = String.valueOf(resultMap.get("year"));
                                month = null;
                            }
                            if(String.valueOf(TimeConst.CALCULATE_TYPE_MONTH).equals(ltn.getPeriodtype())){
                                year = String.valueOf(resultMap.get("year"));
                                month = String.valueOf(resultMap.get("month"));
                            }
                            if(String.valueOf(TimeConst.CALCULATE_TYPE_Entry).equals(ltn.getPeriodtype())){
                                year = String.valueOf(resultMap.get("year"));
                                month = null;
                                staffIdSelect = (List<String>)resultMap.get("data");
                            }
                        }
                        log.error(String.format("%s, 需要计算的假勤方案:%s，休假类型:%s，按入职周年需要计算的人员:%s",logTitle,attendSchemeVO.getName(),JSON.toJSONString(ltn),JSON.toJSONString(staffIdSelect)));
                        calToTask(attendSchemeVO,ltn,staffIdSelect,year,month,isTimeTask);
                    }
                    log.error(String.format("%s,假勤方案-%s已处理...", logTitle, attendSchemeVO.getName()));
                } catch (Exception ex) {
                    String msg = String.format("%s,假勤方案-%s处理失败...", logTitle, attendSchemeVO.getName());
                    log.error(msg, ex);
                }
            }
            callback(1, mqParamsDTO.getLogId(), logTitle, String.format("任务执行完成(%s人)",psnNum));
        } catch (Exception ex) {
            String msg = String.format("%s:生成异常", logTitle);
            log.error(msg,ex);
            callback(0,mqParamsDTO.getLogId(),logTitle,msg);
        }
    }

    @Override
    public List<LeaveBalanceRpt> getLeaveBalanceRpts(List<String> staffIdSelect, String year, String month, LeaveTypeNew ltn){
        Condition cond = new Condition();
        if (CollectionUtils.isNotEmpty(staffIdSelect)) {
            cond.addExpression("b.staffid", staffIdSelect, "in");
        }
        if(org.apache.commons.lang.StringUtils.isNotEmpty(year)){
            cond.addExpression("b.year",year);
        }
        if(org.apache.commons.lang.StringUtils.isNotEmpty(month)){
            cond.addExpression("b.month",month);
        }
        cond.addExpression("b.leavetype",ltn.getId());
        cond.addExpression("b.periodtype",ltn.getPeriodtype());
        // 只处理未结算的额度
        cond.addExpression("b.clearingstate",0);
        List<LeaveBalanceRpt> leaveBalanceRpts = leaveCommonService.queryByConditon(cond,null);
        return leaveBalanceRpts;
    }

    /**
     * @Author: wangwy
     * @param: [ltn, period, attendSchemeVO]
     * @return: java.util.Map<java.lang.String,java.lang.Object>
     * @Date: 2019/8/20 10:50
     * 校验是否符合结算时间规则
     */
    public Map<String, Object> checkToCal(LeaveTypeNew ltn, AttendPeriod period, AttendSchemeVO attendSchemeVO, boolean isSettlement, boolean isTimeTask){
        Map<String, Object> resultMap = new HashMap<>();
        String year = null;
        String month = null;
        resultMap.put("isContinue", false);
        if ("0".equals(ltn.getIslimit())) {
            resultMap.put("isContinue",true);
        }
        // 按考勤年
        if(String.valueOf(TimeConst.CALCULATE_TYPE_YEAR).equals(ltn.getPeriodtype())) {
            //按考勤年，如果是考勤年的第一天则计算，否则继续下一个循环
            Date periodBeginDate = period.getBegindate();
            boolean isNeedCal = checkCalLeaveBalance(periodBeginDate, ltn, isSettlement);
            if(!isNeedCal){
                resultMap.put("isContinue",true);
            }
            if(isSettlement){
                // 年初结算上一年的额度
                year = DateUtil.addYear(period.getYear(), -1);
            }else{
                year = period.getYear();
            }
            resultMap.put("year",year);
        }
        // 按考勤月
        if(String.valueOf(TimeConst.CALCULATE_TYPE_MONTH).equals(ltn.getPeriodtype())) {
            //按考勤月，如果是考勤月的第一天则计算，否则继续下一个循环
            Date periodBeginDate = period.getBegindate();
            log.debug("考勤期间period::_periodBeginDate::"+periodBeginDate);
            boolean isNeedCal = checkCalLeaveBalance(periodBeginDate, ltn, isSettlement);
            if(!isNeedCal){
                resultMap.put("isContinue",true);
            }
            year = period.getYear();
            month = period.getMonth();
            if(isSettlement){
                // 新年第一月，结转上一年12月的额度，否则结转上一月的
                if("1".equals(month)){
                    month = "12";
                    year = DateUtil.addYear(year, -1);
                }else{
                    month = DateUtil.addMonth(year+"-"+month, -1);
                    month = month.split("-")[1];
                }
            }
            resultMap.put("year",year);
            resultMap.put("month",Integer.valueOf(month));
        }
        // 按照入职周年算
        List<String> staffIdSelect = new ArrayList<>();
        if(String.valueOf(TimeConst.CALCULATE_TYPE_Entry).equals(ltn.getPeriodtype())) {
            if(isSettlement){
                // 结算上一年的额度
                year = DateUtil.addYear(period.getYear(), -1);
            }else{
                year = period.getYear();
            }
            List<AttendStaff> attendStaffs = leaveCommonService.filterStaffCondtionByLeaveType(ltn, period, attendSchemeVO.getOrgs(), null, isTimeTask);
            log.error("获取符合条件的人员:"+JSON.toJSONString(attendStaffs));
            // 获取符合条件的人员id集合
            List<String> staffIds = FormulaUtils.getPropertyListFromBeanVO(attendStaffs, "staffId");
            // 获取人员的入职信息
            Map<String, Date> staffJoinOrgMap = leaveCommonService.getJoinDate(staffIds, year);
            // 获取需要计算的人员id
            for (AttendStaff attendStaff : attendStaffs) {
                if (staffJoinOrgMap != null && staffJoinOrgMap.containsKey(attendStaff.getStaffId())) {
                    Date beginDate = staffJoinOrgMap.get(attendStaff.getStaffId());
                    if(beginDate == null){
                        continue;
                    }else{
                        // 入职时间调整到对应的考勤周期，用于判断是否需要结算上一周期
                        SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm:ss");
                        String beginDateStr = period.getYear()+"-"+sdf.format(beginDate);
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        try {
                            beginDate = simpleDateFormat.parse(beginDateStr);
                        } catch (ParseException e) {
                            log.error("入职日期改考勤期间开始日期转换失败！",e);
                            e.printStackTrace();
                        }
                    }
                    boolean isNeedCal = checkCalLeaveBalance(beginDate, ltn, isSettlement);
                    if(isNeedCal){
                        staffIdSelect.add(attendStaff.getStaffId());
                    }
                }
            }
            if(CollectionUtils.isEmpty(staffIdSelect)){
                resultMap.put("isContinue",true);
            }
            resultMap.put("year",year);
            resultMap.put("data",staffIdSelect);
        }
        return resultMap;
    }

    /**
     * @Author: wangwy
     * @param: [begindate]
     * @return: boolean
     * @Date: 2019/8/9 10:07
     * 判断传入时间是否等于当前时间，或者是否大于当前时间并且小于可超期时间
     */
    @Override
    public boolean checkCalLeaveBalance(Date beginDate, LeaveTypeNew leaveType, boolean isSettlement){
        // 如果是结转，判断是否延长有效期
        if(isSettlement){
            //是否延长有效期
            int extendDay = leaveType.getExtendday();
            if(extendDay > 0){
                beginDate = DateUtil.addDay(beginDate, extendDay);
            }
        }
        Date nowDate = new Date();
        // periodEndDate > nowDate 大于 0 periodEndDate < nowDate 小于 0
        log.debug("考勤期间period::_beginDate::"+beginDate+"nowDate::"+nowDate);
        int compareDate = DateUtil.getIntervalDays(beginDate, nowDate);
        if(compareDate>0){
            return true;
        }
        return true;
    }

    /**
     * 休假额度总入口
     *
     * @param schemeId    部门ID
     * @param leaveTypeId 休假类别id
     * @param orgIds      部门id
     * @param staffIds    人员ID
     * @param year        年
     * @param month       月
     * @return
     */
    @Override
    public boolean calTotal(String schemeId, String leaveTypeId, String orgIds,boolean isIncludeSub, String staffIds, String year, String month) {
        boolean re;
        long startCalTotal = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calTotal...");
        List<String> deptIdList = CalendarCommon.getListids(orgIds);
        //获取假勤方案
        long startAttendSchemeVO = System.currentTimeMillis();
        AttendSchemeVO attendSchemeVO = leaveCommonService.getAttendSchemeVO(schemeId, deptIdList,isIncludeSub, true);
        long endAttendSchemeVO = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calTotal...getattendSchemeVO:共用时：" + (endAttendSchemeVO - startAttendSchemeVO));
        // 没有人员计算范围
        if (attendSchemeVO == null || attendSchemeVO.getOrgs() == null || attendSchemeVO.getOrgs().size() == 0) {
            throw new AdderBusinessException("没有可计算人员!", "attend.report.i18n41");
        }
        //获取休假类别
        LeaveTypeNew ltn = leaveTypeNewService.getLeaveTypeNewById(leaveTypeId);
        if (ltn == null) {
            throw new AdderBusinessException("假别类型无效！", "attend.process.i18n0056");
        }
        List<String> staffIdList = CalendarCommon.getListids(staffIds);
        long startCalCommon = System.currentTimeMillis();
        re = calCommon(attendSchemeVO,ltn,staffIdList, year, month,null, false);
        long endCalCommon = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calTotal...calCommon ,共用时：" + (endCalCommon - startCalCommon));
        long endCalTotal = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calTotal...calTotal ,共用时：" + (endCalTotal - startCalTotal));
        return re;
    }
    /**
     * 休假额度总入口
     * @param year        年
     * @param month       月
     * @return
     */
    @Override
    public boolean calToTask(AttendSchemeVO attendSchemeVO, LeaveTypeNew ltn, List<String> staffIdSelect, String year, String month, boolean isTimeTask) {
        boolean result = false;
        log.debug("###...LeaveBlanceRptCal...calToTask...");
        if (attendSchemeVO == null || attendSchemeVO.getOrgs() == null || attendSchemeVO.getOrgs().size() == 0) {
            return result;
        }
        long startCalCommon = System.currentTimeMillis();
        result = calCommon(attendSchemeVO,ltn,staffIdSelect, year, month,null, isTimeTask);
        long endCalCommon = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calToTask...calCommon ,共用时：" + (endCalCommon - startCalCommon));
        return result;
    }
    @Override
    @Transactional
    public boolean calCommon(AttendSchemeVO attendSchemeVO, LeaveTypeNew ltn,List<String> staffIdSelect, String year, String month,Map<String,BigDecimal> preValueMap, boolean isTimeTask) {
        boolean result = true;
        log.debug("###...LeaveBlanceRptCal...calCommon...");
        //获取考勤期间有效日期
        AttendPeriod attendPeriod = leaveCommonService.getAttendPeriod(attendSchemeVO.getPeriodSchemeId(), year, month);
        if (attendPeriod == null) {
            throw new AdderBusinessException("请先设置该年的考勤期间！", "attend.process.i18n0141");
        }
        try {
            log.debug("###...LeaveBlanceRptCal...calCommon...考勤周期：" + JSON.toJSONString(attendPeriod));
            //根据休假类别获取人员的过滤条件
            List<String> orgs = attendSchemeVO.getOrgs();
            //定义公共变量//休假类型的Id;
            String letypeId = ltn.getId();
            //休假额度的休假类型
            boolean isCal = getIsCal(ltn);
            //定义休假额度计算参数
            LeaveBalanceRptCalParams rptCalParams = leaveCommonService.getLeaveBalanceRptCalParams(staffIdSelect, orgs, attendPeriod, isTimeTask);
            dealLeaveBalanceRptCalParams(rptCalParams, ltn, year);
            //获取可计算的人员范围
            long startAttendStaffs = System.currentTimeMillis();
            List<AttendStaff> attendStaffs = leaveCalHelpService.getAttendStaffCallData(rptCalParams);
            long endAttendStaffs = System.currentTimeMillis();
            log.debug("###...LeaveBlanceRptCal...calCommon...getAttendStaffs ,共用时：" + (endAttendStaffs - startAttendStaffs));
            //删掉其他考勤方式的休假额度，每种休假类型对应一种考勤期间，如果考勤期间变了，老的数据删除重新计算
            deletOtherPeriodtype(letypeId, ltn.getPeriodtype());
            log.debug(".........deletOtherPeriodtype已完成.............");
            //获取符合条件的人员id集合
            List<String> staffIds = FormulaUtils.getPropertyListFromBeanVO(attendStaffs, "staffId");
            List<LeaveBalanceRpt> leaveBalanceRptList = leaveCommonService.getLeaveBalanceRptList(orgs, staffIdSelect, year, month, letypeId, isTimeTask);
            //删除多余的存休额度
            deleteRedundantBlancesData(leaveBalanceRptList);
            //获取已有的休假额度
            Map<String, LeaveBalanceRpt> leaveBalanceRptMap = leaveCommonService.dealLeaveBalanceRpt(leaveBalanceRptList, false);
            // 如果请的假时默认假别，引用过公共休假类型的所有休假类型
            List<LeaveTypeNew> leaveTypeNewList = leaveCommonService.getAboutLeaveType(ltn);
            List<String> leaveTypeIdList = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(leaveTypeNewList)){
                leaveTypeIdList = leaveTypeNewList.stream().map(LeaveTypeNew::getId).collect(Collectors.toList());
            }
            //获取请假单详情
            Map<String, CalValueByState> calValueByStateMap = leaveCommonService.getLeaveApplyDetailBalance(letypeId, leaveTypeIdList, staffIds);
            log.debug("###...LeaveBlanceRptCal...calCommon...calValueByStateMap：" + JSON.toJSONString(calValueByStateMap));
            Map<String, BigDecimal> overtimeBillRestBalanceMap = new HashMap<>();
            //计算额度,预置数据
            log.debug("初始化数据开始");
            Map<String, Map<String, Object>> allDatas = new ConcurrentHashMap<>(12);
            //加班转调休的不走初始化
            if (!"10".equals(ltn.getCode())) {
                if (isCal) {
                    initPubEnvironmentVariables(ltn.getFormula(), attendSchemeVO, staffIds, attendPeriod);
                }
            } else {
                overtimeBillRestBalanceMap = leaveCommonService.getOvertimeBillRestBalance(attendSchemeVO.getOvertimeRuleId(), staffIds, year, month);
                log.debug("###...LeaveBlanceRptCal...calCommon...overtimeBillRestBalanceMap：" + JSON.toJSONString(overtimeBillRestBalanceMap));
            }
            log.debug("初始化数据结束");
            dealCal(attendStaffs, leaveBalanceRptMap, calValueByStateMap, overtimeBillRestBalanceMap, preValueMap, ltn, isCal, attendSchemeVO.getId(), year, month);
        } catch (Exception e) {
            result = false;
            log.error("休假额度计算异常###...LeaveBlanceRptCal...calCommon...{}", e);
            throw new AdderBusinessException("公式计算失败！", "attend.process.i18n0159");
        }
        return result;
    }
    private void dealLeaveBalanceRptCalParams(LeaveBalanceRptCalParams rptCalParams,LeaveTypeNew ltn,String year){
        rptCalParams.setPeriodType(ltn.getPeriodtype());
        rptCalParams.setIsextensionvalidity(ltn.getIsextensionvalidity());
        rptCalParams.setExtendday(ltn.getExtendday());
        rptCalParams.setYear(year);
    }
    private void dealCal(List<AttendStaff> attendStaffs,
                         Map<String, LeaveBalanceRpt> leaveBalanceRptMap,
                         Map<String, CalValueByState> calValueByStateMap,
                         Map<String, BigDecimal> overtimeBillRestBalanceMap,
                         Map<String,BigDecimal> preValueMap,
                         LeaveTypeNew ltn,
                         boolean isCal,
                         String attendSchemeVOId,
                         String year,
                         String month){
        //休假类型的Id;
        String leaveTypeId=ltn.getId();
        //获取休假类型的公式
        String formula=ltn.getFormula();
        boolean isManual=TimeConst.LEAVE_MIN_SOURCE_Manual.equals(ltn.getSource())?true:false;

        List<LeaveBalanceRpt> leaveBalanceRpts = new ArrayList<>();
        String key;
        List<String> staffCons = new ArrayList<>();

        //获取当前时间点
        Date datenow=new Date();
        //公式计算参数
        Map<String,Object> calParams=new HashMap<>();
        long startForCal = System.currentTimeMillis();
        for (AttendStaff attendStaff : attendStaffs) {
            //获取计算初始值
            BigDecimal curquotaValue=BigDecimal.ZERO;
            if(!isCal&&!isManual){
                curquotaValue= TypeConvert.parseBigDecimal(formula);
            }
            String staffId=attendStaff.getStaffId();
            if (staffCons.contains(staffId)) {
                continue;
            }
            staffCons.add(staffId);
            key=LeaveCalHelper.getKey(staffId,null,year,month);
            
            log.debug("###休假额度计算###LeaveBlanceRptCal###calCommon###staffId：{}",staffId);
            //获取档案的基本信息
            LeaveBalanceRpt leaveBalanceRpt = null;
            if (MapUtils.isNotEmpty(leaveBalanceRptMap)) {
                leaveBalanceRpt = leaveBalanceRptMap.get(key);
                //已结算状态的不在计算
                if (leaveBalanceRpt != null && leaveBalanceRpt.getClearingstate() == TimeConst.Leave_State_YES) {
                    continue;
                }
                if(leaveBalanceRpt!=null&&isManual){
                    curquotaValue=leaveBalanceRpt.getCurquota();
                }
            }
            //初始化休假额度的基础数据
            leaveBalanceRpt = leaveCommonService.initLeaveBalance(leaveBalanceRpt,attendSchemeVOId,ltn,attendStaff, year, month);
            log.debug("###休假额度计算###LeaveBlanceRptCal###calCommon###开始处理本期额度");
            if ("10".equals(ltn.getCode())) {
                //加班转调休单独处理
                if (overtimeBillRestBalanceMap.containsKey(staffId) && overtimeBillRestBalanceMap.get(staffId).compareTo(BigDecimal.ZERO) == 1) {
                    leaveBalanceRpt.setCurquota(overtimeBillRestBalanceMap.get(staffId));
                }else{
                    // 如果没有加班单，本期额度应该是0
                    leaveBalanceRpt.setCurquota(BigDecimal.ZERO);
                }
            } else {
                //计算本期额度
                if (isCal) {
                    Object value = 0;
                    try{
                        doExtCalParamsMap(datenow,attendStaff,calParams);
                        value = super.calculate(staffId,formula,calParams);
                    }catch (Exception e){
                        log.error("公式计算额度失败！tenantId:{},staffId:{},letypeId:{},stackInfo:{}",AppContext.getContext().getScope(),staffId,leaveTypeId,e);
                        e.printStackTrace();
                    }
                    leaveBalanceRpt.setCurquota(new BigDecimal(String.valueOf(value)));
                } else{
                    leaveBalanceRpt.setCurquota(curquotaValue);
                }
                //add by chenzf@获取调休汇总数据 2021-9-30 17:59:58 begin
                if("可调休天数".equals(ltn.getTypeName())) {
    	        	log.debug("###...LeaveBlanceRptCal...dealCal...listMap...begin");
    	        	String month1 = month;
    	        	List<Map> listMap = new ArrayList();
    	        	if(month != null && month.length() == 1) {
    	        		month1 = "0" + month;
    	        		listMap = attendRptCalDao.queryGlbdef3(staffId, year, month1);
    	        	}
    	            if(StringUtils.isBlank(month1)) {
    	            	listMap = attendRptCalDao.queryYearGlbdef3(staffId, year);
    	            }
    	            attendStaff.setAdjustTime(BigDecimal.ZERO.doubleValue());
    	            if(listMap != null && listMap.size() > 0) {
    	            	log.debug("###...LeaveBlanceRptCal...dealCal...listMap...glbdef3：" + JSON.toJSONString(listMap));
    	            	Map map = listMap.get(0);
    	            	if(map != null) {
    	                	log.debug("###...LeaveBlanceRptCal...map...glbdef3：" + JSON.toJSONString(map));
    	            		BigDecimal glbdef10 = new BigDecimal(map.get("GLBDEF10") == null? "0" : map.get("GLBDEF10").toString());
    	            		BigDecimal glbdef11 = new BigDecimal(map.get("GLBDEF11") == null? "0" : map.get("GLBDEF11").toString());
    	            		BigDecimal glbdef12 = new BigDecimal(map.get("GLBDEF12") == null? "0" : map.get("GLBDEF12").toString());
    	            		attendStaff.setAdjustTime(glbdef10.add(glbdef11).add(glbdef12).doubleValue());
    	            	}
    	            }
    	            //add by chenzf@获取调休汇总数据 2021-9-30 17:59:58 end
    	            log.debug("可调休天数:" + attendStaff.getAdjustTime());
    	            BigDecimal curquota = new BigDecimal(attendStaff.getAdjustTime()).divide(new BigDecimal(7),2,BigDecimal.ROUND_HALF_UP);
    	            log.debug("可调休天数，除以7:" + curquota.setScale(1,BigDecimal.ROUND_HALF_UP));
    	            leaveBalanceRpt.setCurquota(curquota.setScale(1,BigDecimal.ROUND_HALF_UP));
                }
            }
            log.debug("###休假额度计算###LeaveBlanceRptCal###calCommon###Curquota：{}",leaveBalanceRpt.getCurquota());
            //处理结转的上期额度
            if(MapUtils.isNotEmpty(preValueMap)) {
                if(preValueMap.containsKey(staffId)) {
                    leaveBalanceRpt.setPreforwardquota(preValueMap.get(staffId));
                }
            }
            leaveBalanceRpt.setPrerestquota(leaveBalanceRpt.getPreforwardquota());
            //计算已休额度
            key=LeaveCalHelper.getKey(staffId,leaveTypeId,year,month);
            leaveBalanceRpt = leaveCommonService.dealLeaveApply(leaveBalanceRpt, calValueByStateMap.get(key));
            leaveBalanceRpts.add(leaveBalanceRpt);
            if (leaveBalanceRpts.size() > 200) {
                leaveCommonService.batchInsert(leaveBalanceRpts);
                leaveBalanceRpts.clear();
            }
        }
        if (CollectionUtils.isNotEmpty(leaveBalanceRpts)) {
            leaveCommonService.batchInsert(leaveBalanceRpts);
            leaveBalanceRpts.clear();
        }
        long endForCal = System.currentTimeMillis();
        log.debug("###...LeaveBlanceRptCal...calCommon...forcal ,共用时：" + (endForCal - startForCal));
    }

    private void doExtCalParamsMap(Date datenow,AttendStaff attendStaff,Map<String,Object> calParams){
        calParams.clear();
        Date enddate=attendStaff.getEnddate();
        calParams.put("periodBeginDate", attendStaff.getBegindate());
        calParams.put("periodEndDate", enddate);
        if(datenow.before(enddate)){
            calParams.put("realEndDate", datenow);
        } else{
            calParams.put("realEndDate", enddate);
        }
    }

    //加班转调休专用
    @Override
    public List<String> calOvertimeToRest(List<OvertimeSplitBillVO> list)
    {
        log.debug("###.........加班转调休专用开始...calOvertimeToRest.....start：");
        log.debug("###.........加班转调休专用开始...calOvertimeToRest.....list："+JSON.toJSONString(list));
        List<String> biillIds=new ArrayList<>();
        if (list == null || list.isEmpty()) {
            return biillIds;
        }
        biillIds=leaveCommonService.getLeaveBalanceRptsByOvetime(list);
        log.debug("###.........加班转调休专用开始...calOvertimeToRest.....end：");
        return biillIds;
    }
    //请假单保存计算
    @Override
    public String calOneLeaveapply(LeaveApply apply)
    {
        String error = "";
        String leavetypeid= apply.getLeavetypeid();
        LeaveTypeNew ltn = leaveTypeNewService.getLeaveTypeNewById(leavetypeid);
        if(LeaveTypeNew.STORE_LEAVE_CODE.equals(ltn.getCode())) {
            //calOneLeaveapplyForStore(apply);
        }
        else {
            calOneLeaveapplyForBlance(apply);
        }
        return error;
    }
    @Async
    @Override
    public String calAsyncOneLeaveapply(String tenantid,String userid,LeaveApply apply) {
        AppContext.getContext().setUserId(userid);
        AppContext.getContext().setScope(tenantid);
        return calOneLeaveapply(apply);
    }
    @Override
    public String calOneLeaveapply(String tenantid,String userid,LeaveApply apply) {
        AppContext.getContext().setUserId(userid);
        AppContext.getContext().setScope(tenantid);
        return calOneLeaveapply(apply);
    }
    private String calOneLeaveapplyForBlance(LeaveApply apply) {
        String error = "";
        try {
            Date begin = apply.getLeavebegintime();
            Date end = apply.getLeaveendtime();
            String staffid = apply.getStaffid();
            log.info(apply.getLeavetypeid() + "=calOneLeaveapply获取当前请假的额度开始=" + staffid);
            // 首先获取当前请假的额度
            // List<LeaveBalanceRpt> balanceTable = leaveCommonService.querBalanceRestquota(staffid, apply.getLeavetypeid(), begin, end, 0);
            // 提交请假单时，验证休假不限制请假时间，只要有休假额度就可以用
            List<LeaveBalanceRpt> balanceTable = leaveCommonService.queryBalanceRestQuotaForLeaveSave(apply.getStaffid(), apply.getLeavetypeid(), 0);
            log.info("calOneLeaveapply获取当前请假的额度结束");
            log.info(apply.getLeavetypeid() + "=获取getLeaveApplyDetailBalance信息开始=" + staffid);
            // 如果请的假时默认假别，引用过公共休假类型的所有休假类型
            LeaveTypeNew leaveTypeNew = leaveTypeNewService.getLeaveTypeNewById(apply.getLeavetypeid());
            List<LeaveTypeNew> leaveTypeNewList = leaveCommonService.getAboutLeaveType(leaveTypeNew);
            List<String> leaveTypeIdList = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(leaveTypeNewList)){
                leaveTypeIdList = leaveTypeNewList.stream().map(LeaveTypeNew::getId).collect(Collectors.toList());
            }
            Map<String, CalValueByState> calValueByStateMap = leaveCommonService.getLeaveApplyDetailBalance(apply.getLeavetypeid(),leaveTypeIdList, Arrays.asList(staffid));
            log.info("获取getLeaveApplyDetailBalance信息结束");
            String key;
            List<LeaveBalanceRpt> leaveBalanceRptsNew = new ArrayList<>();
            List<String> ids = new ArrayList<>();
            for (LeaveBalanceRpt leaveBalanceRpt : balanceTable) {
                if (ids.contains(leaveBalanceRpt.getId())) {
                    continue;
                }
                key = leaveBalanceRpt.getStaffid() + "_";
                key += apply.getLeavetypeid() + "_";
                if (StringUtils.isNotBlank(leaveBalanceRpt.getYear())) {
                    key += leaveBalanceRpt.getYear();
                }
                if (StringUtils.isNotBlank(leaveBalanceRpt.getMonth())) {
                    key += leaveBalanceRpt.getMonth();
                }
                leaveCommonService.dealLeaveApply(leaveBalanceRpt, calValueByStateMap.get(key));
                ids.add(leaveBalanceRpt.getId());
                leaveBalanceRptsNew.add(leaveBalanceRpt);
            }
            if (CollectionUtils.isEmpty(leaveBalanceRptsNew)) {
                return error;
            }
            log.info("calOneLeaveapply更新休假额度开始");
            leaveCommonService.batchInsertNew(leaveBalanceRptsNew);
            log.debug("leaveBalanceRptsNew:"+JSON.toJSONString(leaveBalanceRptsNew));
            log.info("calOneLeaveapply更新休假额度结束");
            leaveBalanceRptsNew.clear();
        } catch (Exception e) {
            log.error(e.getMessage());
            error = e.getMessage();
        }
        return error;
    }
    //region 数据删除
    //删掉其他考勤方式的
     private void deletOtherPeriodtype(String leaveTypeId,String periodType) {
        Condition deCond=new Condition();
        deCond.addExpression("leavetype",leaveTypeId);
        deCond.addExpression("periodtype",periodType,"<>");
        dao.deleteRptByCond(deCond);
    }
    private void deletCalBlancedata(List<String> deptIds,List<String> staffIds,String leaveTypeId,String year,String month) {
        Condition deCond=new Condition();
        if (CollectionUtils.isNotEmpty(staffIds)) {
            deCond.addExpression("b.staffid", staffIds, "in");
        }
        if (CollectionUtils.isNotEmpty(deptIds)) {
            deCond.addExpression("attend.dept_id", deptIds, "in");
        }
        if (StringUtils.isNotBlank(month)) {
            deCond.addExpression("b.month", month);
        }
        deCond.addExpression("b.year", year);
        deCond.addExpression("b.leavetype", leaveTypeId);
        dao.deleteRptAndASByCond(deCond);
    }
    //endregion

    //寻找跨方案人员，并转移其数据（未结算）
    private void turnStaffOtherA(List<String> staffids,String attendSchemeId,LeaveTypeNew leaveTypeNew)
    {
        Condition condK=new Condition();
        condK.addExpression("staffid",staffids,"in");
        condK.addExpression("leaverefid",leaveTypeNew.getRefId());
        condK.addExpression("schemeid",attendSchemeId,"<>");
        condK.addExpression("periodtype",leaveTypeNew.getPeriodtype());
        condK.addExpression("clearingstate",false);
        condK.addExpression("invalidstate",false);
        List<LeaveBalanceRpt> leaveBalanceRptKs=genericService.find(LeaveBalanceRpt.class,condK,null);
        if(!leaveBalanceRptKs.isEmpty())
        {
            for(LeaveBalanceRpt leaveBalanceRpt:leaveBalanceRptKs)
            {
                leaveBalanceRpt.setSchemeid(attendSchemeId);
                leaveBalanceRpt.setLeavetype(leaveTypeNew.getId());
            }
            leaveCommonService.batchInsertNew(leaveBalanceRptKs);
        }
    }
    private boolean isAttendStaff(AttendStaff attendStaff,Date begindate,Date enddate) {
        boolean isadd = true;
        if (attendStaff.getBegindate() != null && attendStaff.getBegindate().getTime() > enddate.getTime()) {
            isadd = false;
        }
        if (attendStaff.getEnddate() != null && attendStaff.getEnddate().getTime() < begindate.getTime()) {
            isadd = false;
        }
        return isadd;
    }
    /***
     * 删除多余的存休额度
     * @param leaveBalanceRpts
     */
    private void deleteRedundantBlancesData(List<LeaveBalanceRpt> leaveBalanceRpts) {
        //获取已存在的人员的存休额度
        if (CollectionUtils.isEmpty(leaveBalanceRpts)) {
            return;
        }
        // 只处理未结算，未作废的(SUPPORT-47255,这里如果删除，后边已结算跳过计算，就会导致已结算消失)
        leaveBalanceRpts = leaveBalanceRpts.stream().filter(value -> "0".equals(value.getInvalidstate()) && "0".equals(value.getClearingstate())).collect(Collectors.toList());
        leaveCommonService.batchDeleteList(leaveBalanceRpts);
    }


    /***
     * 回调反馈执行结果
     * @param status status
     * @param logId logId
     * @param title title
     * @param content content
     */
    private void callback(int status, String logId,String title,String content) {
        if (StringUtils.isBlank(logId)) {
            return;
        }
        log.error(String.format("%s:生成结束，准备回写任务状态", title));
        if(!logId.startsWith("custome")) {
            JSONObject param = new JSONObject();
            param.put("status", status);
            param.put("id", logId);
            param.put("title", title);
            param.put("content", content);
            try {
                String url = PropertyUtil.getPropertyByKey("dispatch.task.updateTaskLog", "http://workbench.yyuap.com/warning/warning/async/updateTaskLog");
                Map<String, String> headMap = new HashMap<>();
                headMap.put("Content-Type", "application/json;charset=UTF-8");
                String result = HttpClientUtils.doPost(url, param.toJSONString(), headMap);
                log.error(String.format("%s:任务状态回写完成:url-%s,,param-%s,,result-%s", title, url, param, result));
            } catch (Exception e) {
                log.error(String.format("%s:回写异常-%s", title, e.getMessage()), e);
            }
        }
        //更新任务日志状态
        updateTaskLog(logId,status,content);
        log.error(String.format("%s:任务日志更新完毕", title));
    }

    private void logTask(MQParamsDTO dto){
        TaskLogVO taskLogVO = new TaskLogVO();
        taskLogVO.setTenantid(AppContext.getContext().getScope());
        taskLogVO.setCreator(AppContext.getContext().getUserId());
        taskLogVO.setTaskId(dto.getLogId());
        taskLogVO.setSourceType(dto.getSourceType());
        taskLogVO.setCalType(dto.getCalType());
        taskLogVO.setFinish(false);
        taskLogVO.setMsg("进行中");
        //taskLogService.insert(taskLogVO);
    }

    private void updateTaskLog(String logId,int status,String msg){
        TaskLogVO taskLogVO = new TaskLogVO();
        taskLogVO.setTaskId(logId);
        taskLogVO.setFinish(status == 1);
        taskLogVO.setMsg(msg);
        taskLogVO.setTenantid(AppContext.getContext().getScope());
        taskLogVO.setCreator (AppContext.getContext().getUserId());
        //taskLogService.update(taskLogVO);
    }

    private String getLogTitle(MQParamsDTO mqParamsDTO){
        return String.format("####leaveblance-%s([%s]-%s)", "休假额度定时计算任务", mqParamsDTO.getTenantId(), mqParamsDTO.getLogId());
    }
    //endregion
    protected void initPubEnvironmentVariables(String formula,AttendSchemeVO attendSchemeVO,List<String> stringList,AttendPeriod attendPeriod) {
        tsAttendRptBasicCalParamVO = new TsAttendRptBasicCalParamVO();
        tsAttendRptBasicCalParamVO.setYear(attendPeriod.getYear());
        tsAttendRptBasicCalParamVO.setMonth(attendPeriod.getMonth());
        tsAttendRptBasicCalParamVO.setBeginDate(attendPeriod.getBegindate());
        tsAttendRptBasicCalParamVO.setEndDate(attendPeriod.getEnddate());
        tsAttendRptBasicCalParamVO.setFormulaVariableXmlPath(ITsFormulaConstant.FORMULA_VARIABLE_XML_PATH_LEAVERULE);
        tsAttendRptBasicCalParamVO.setAttendSchemeVO(attendSchemeVO);
        tsAttendRptBasicCalParamVO.setAttendPeriod(attendPeriod);
        super.initPubEnvironmentVariables(formula,stringList,tsAttendRptBasicCalParamVO);
    }
    private void initPubEnvironmentVariablesNew(Map<String, Map<String, Object>> allDatas,LeaveBalanceRptCalParams rptCalParams,List<AttendStaff> attendStaffs,String formula) {

    }
    //判断是否要进行初始化公式计算
    private boolean getIsCal(LeaveTypeNew ltn){
        //判断是否为手工录入的
        boolean isCal=TimeConst.LEAVE_MIN_SOURCE_Formula.equals(ltn.getSource())?true:false;
        //判断公式内容是否为空
        if(isCal) {
            isCal = StringUtils.isNoneBlank(ltn.getFormula()) ? true : false;
        }
        //判断公式内容是否为纯数字的
        if(isCal){
            if(LeaveCalHelper.isNumber(ltn.getFormula())){
                isCal=false;
            }
        }
        return isCal;
    }
}
