package com.mortals.xhx.module.attendance.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dingtalkworkflow_1_0.models.ListProcessInstanceIdsResponse;
import com.aliyun.dingtalkworkflow_1_0.models.ListProcessInstanceIdsResponseBody;
import com.google.common.collect.Lists;
import com.mortals.framework.common.Rest;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.DataUtil;
import com.mortals.framework.util.DateUtils;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.util.ThreadPool;
import com.mortals.xhx.base.system.user.model.UserEntity;
import com.mortals.xhx.base.system.user.service.UserService;
import com.mortals.xhx.common.code.*;
import com.mortals.xhx.common.utils.AddAttendThread;
import com.mortals.xhx.common.utils.AttendSummaryThread;
import com.mortals.xhx.module.attendance.model.*;
import com.mortals.xhx.module.attendance.model.vo.AttendanceSummaryQuery;
import com.mortals.xhx.module.attendance.service.AttendanceGroupStaffService;
import com.mortals.xhx.module.attendance.service.AttendanceRecordHikService;
import com.mortals.xhx.module.attendance.service.AttendanceStatService;
import com.mortals.xhx.module.check.model.vo.StaffCheckSummaryQuery;
import com.mortals.xhx.module.dingding.personal.service.IDingPersonService;
import com.mortals.xhx.module.perform.model.PerformAttendRecordEntity;
import com.mortals.xhx.module.perform.model.PerformAttendRecordQuery;
import com.mortals.xhx.module.perform.model.PerformRulesEntity;
import com.mortals.xhx.module.perform.model.PerformRulesQuery;
import com.mortals.xhx.module.perform.service.PerformAttendRecordService;
import com.mortals.xhx.module.perform.service.PerformRulesService;
import com.mortals.xhx.module.staff.model.StaffEntity;
import com.mortals.xhx.module.staff.service.StaffService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.xhx.module.attendance.dao.AttendanceLeaveRecordDao;
import com.mortals.xhx.module.attendance.service.AttendanceLeaveRecordService;
import org.springframework.util.StopWatch;

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

import static com.mortals.xhx.common.key.RedisKey.KEY_ATTENDANCE_STAT_CACHE;
import static com.mortals.xhx.common.key.RedisKey.KEY_CHECK_SUMMARY_CACHE;

/**
 * AttendanceLeaveRecordService
 * 请假记录信息 service实现
 *
 * @author zxfei
 * @date 2023-04-07
 */
@Service("attendanceLeaveRecordService")
@Slf4j
public class AttendanceLeaveRecordServiceImpl extends AbstractCRUDServiceImpl<AttendanceLeaveRecordDao, AttendanceLeaveRecordEntity, Long> implements AttendanceLeaveRecordService {

    @Autowired
    private AttendanceStatService attendanceStatService;
    @Autowired
    private ICacheService cacheService;
    @Autowired
    private StaffService staffService;
    @Autowired
    private UserService userService;
    @Autowired
    private AttendanceRecordHikService hikService;
    @Autowired
    private IDingPersonService dingPersonService;
    @Autowired
    private PerformAttendRecordService performAttendRecordService;
    @Autowired
    private AttendanceGroupStaffService attendanceGroupStaffService;
    @Autowired
    private PerformRulesService performRulesService;

    /**
     * 员工事假请假数据key 员工id+开始时间+结束时间
     */
    private static String LEAVE_REIDS_KEY = "attendance:leave:";

    @Override
    public AttendanceLeaveRecordEntity update(AttendanceLeaveRecordEntity entity, Context context) throws AppException {
        this.updateBefore(entity, context);
        boolean doPerformAttend = true;
        AttendanceLeaveRecordEntity temp = this.get(entity.getId());
        if (temp != null) {
            boolean leaveType = LeaveTypeEnum.事假.getValue() == temp.getLeaveType() || LeaveTypeEnum.病假.getValue() == temp.getLeaveType();
            if (AppealResultEnum.通过.getValue() == temp.getAuditResult() && leaveType) {
                doPerformAttend = false;
            }
        }
        int iRet = this.dao.update(entity);
        if (iRet == 0) {
            throw new AppException(-1002, "更新失败！");
        } else {
            super.updateAfter(entity, context);
            if (doPerformAttend) {
                creatPerformAttend(entity, context);
            }
            doUpdateRecordAndSummary(entity, context);
            return entity;
        }
    }

    @Override
    public AttendanceLeaveRecordEntity doUpdateRecord(String processInstanceId, AttendanceLeaveRecordEntity leaveRecordEntity) throws Exception {
        if (leaveRecordEntity.getAuditResult() != AppealResultEnum.审核中.getValue()) {
            //临时解决并发问题，更新的都等待一秒后再执行
            Thread.sleep(1000);
        }
        synchronized (this) {
            AttendanceLeaveRecordEntity recordEntity = this.selectOne(new AttendanceLeaveRecordQuery().remark(processInstanceId));
            if (ObjectUtils.isEmpty(recordEntity)) {
                leaveRecordEntity.setCreateTime(new Date());
                leaveRecordEntity.setCreateUserId(1L);
                this.save(leaveRecordEntity);
            } else {
                leaveRecordEntity.setId(recordEntity.getId());
                leaveRecordEntity.setUpdateUserId(1L);
                leaveRecordEntity.setUpdateTime(new Date());
                this.update(leaveRecordEntity);
            }
            return leaveRecordEntity;
        }
    }


    @Override
    protected void saveAfter(AttendanceLeaveRecordEntity entity, Context context) throws AppException {
        super.saveAfter(entity, context);
        creatPerformAttend(entity, context);
        doUpdateRecordAndSummary(entity, context);
    }

    /**
     * 更新汇总信息
     *
     * @param entity
     * @param context
     */
    private void doUpdateRecordAndSummary(AttendanceLeaveRecordEntity entity, Context context) {
        //请假是未来 不做处理
        int compare = DateUtil.compare(entity.getStartTime(), new Date());
        if (compare > 0) {
            //请假未来 不做处理
            log.info("请假未来 不做处理! {}", entity.getLeavePerson());
            return;
        }
        //更新考勤记录与异常记录
        if (AppealResultEnum.通过.getValue() == entity.getAuditResult() || AppealResultEnum.撤销审批通过.getValue() == entity.getAuditResult()) {
            AttendanceRecordHikEntity hikEntity = new AttendanceRecordHikEntity();
            hikEntity.setStaffId(entity.getLeavePersonId());
            //判断请假的开始日期与结束日期
  /*          boolean in = DateUtil.isIn(new Date(), entity.getStartTime(), entity.getEndTime());
            if (in) {
                //当前日期在请假期间，计算考勤结束时间为当前时间;
                attendanceRecordHikQuery.setAttendanceDateStart(DateUtil.formatDate(entity.getStartTime()));
                attendanceRecordHikQuery.setAttendanceDateEnd(DateUtil.today());
            } else {
                attendanceRecordHikQuery.setAttendanceDateStart(DateUtil.formatDate(entity.getStartTime()));
                attendanceRecordHikQuery.setAttendanceDateEnd(DateUtil.formatDate(entity.getEndTime()));
            }*/

            DateTime attendStart = DateUtil.parseDate(DateUtil.formatDate(entity.getStartTime()));
            DateTime attendEnd = DateUtil.parseDate(DateUtil.formatDate(entity.getEndTime()));
            Long compare1 = DateUtil.betweenDay(attendEnd, attendStart, true);
            log.info("考勤计算天数区间：{}", compare1);
            for (int i = 0; i <= compare1.intValue(); i++) {
                DateTime curDate = DateUtil.offsetDay(attendStart, i);
                hikEntity.setAttendanceDateStart(curDate.toDateStr());
                hikEntity.setAttendanceDateEnd(curDate.toDateStr());
                hikEntity.setOrderColList(Arrays.asList(new OrderCol("attendanceDate")));
                try {
                    hikService.addAttendanceRecordByQuery(hikEntity, context);
                } catch (Exception e) {
                    log.error("同步请假记录异常", e);

                }
            }
            log.info("请假通过后汇总考勤，请假人员：" + entity.getLeavePerson());
            AttendanceSummaryQuery query = new AttendanceSummaryQuery();
            query.setSummaryTimeEnd(DateUtils.getStrDate(entity.getEndTime()));
            query.setSummaryTimeStart(DateUtils.getStrDate(entity.getStartTime()));
            query.setStaffId(entity.getLeavePersonId());
            cacheService.lpush(KEY_ATTENDANCE_STAT_CACHE, query);
      /*      AddAttendThread addAttendThread = new AddAttendThread(hikService, attendanceRecordHikQuery, context);
            ThreadPool.getInstance().execute(addAttendThread);*/
        }
    }


    @Override
    public Rest<String> syncLeaveRecord(AttendanceLeaveRecordQuery query, Context context) {
        List<AttendanceLeaveRecordEntity> waitSyncLeaveRecords = new ArrayList<>();
        Long staffId = query.getStaffId();
        List<StaffEntity> staffCacheList = staffService.getCacheList();
        // List<UserEntity> userCacheList = userService.getCacheList();
        if (ObjectUtils.isEmpty(staffId)) {
            //同步所有用户
            List<StaffEntity> userEntityList = staffCacheList.stream()
                    .filter(f -> !ObjectUtils.isEmpty(f.getDingUserId())).collect(Collectors.toList());
            getLeaveRecordByUserList(query, userEntityList, waitSyncLeaveRecords);
        } else {
            //同步指定用户
            List<StaffEntity> userEntityList = staffCacheList.stream()
                    .filter(f -> !ObjectUtils.isEmpty(f.getDingUserId()))
                    .collect(Collectors.toList());
            getLeaveRecordByUserList(query, userEntityList, waitSyncLeaveRecords);
        }
        log.info("待同步请假记录：{}", JSON.toJSONString(waitSyncLeaveRecords));
        if (!ObjectUtils.isEmpty(waitSyncLeaveRecords)) {
            for (AttendanceLeaveRecordEntity waitSyncLeaveRecord : waitSyncLeaveRecords) {
                dingPersonService.handleByProcessInstanceId(waitSyncLeaveRecord.getRemark());
            }
        }
        return Rest.ok();
    }

    @Value("${source.type:''}")
    protected String sourceType;

    private void getLeaveRecordByUserList(AttendanceLeaveRecordQuery query, List<StaffEntity> staffCacheList, List<AttendanceLeaveRecordEntity> waitSyncLeaveRecords) {
        List<List<StaffEntity>> partition = Lists.partition(staffCacheList, 10);
        for (List<StaffEntity> staffList : partition) {
            //String dingUserId = item.getDingUserId();
            List<String> dingUserIds = staffList.stream().map(m -> m.getDingUserId()).collect(Collectors.toList());
            long startTime = DateUtil.parseDateTime(query.getStartTimeStart() + " 00:00:00").getTime();
            long endTime = DateUtil.parseDateTime(query.getEndTimeEnd() + " 23:59:59").getTime();
            Long nextToken = 0L;
            long maxResults = 20L;
            if (AttendSourceTypeEnum.海康云平台.getValue().equals(sourceType)) {

                for (ProcessInstanceEnum processInstanceEnum : ProcessInstanceEnum.values()) {
                    try {
                        getRecords(waitSyncLeaveRecords, processInstanceEnum, startTime, endTime, nextToken, maxResults, dingUserIds);
                    } catch (Exception e) {
                        log.error("同步请假记录异常", e);
                    }
                }
            }  else if (AttendSourceTypeEnum.钉钉.getValue().equals(sourceType)) {
                for (QiongLaiProcessInstanceEnum processInstanceEnum : QiongLaiProcessInstanceEnum.values()) {
                    try {
                        getRecords(waitSyncLeaveRecords, processInstanceEnum, startTime, endTime, nextToken, maxResults, dingUserIds);
                    } catch (Exception e) {
                        log.error("同步请假记录异常", e);
                    }
                }
            }else {
                for (ProcessInstanceEnum processInstanceEnum : ProcessInstanceEnum.values()) {
                    try {
                        getRecords(waitSyncLeaveRecords, processInstanceEnum, startTime, endTime, nextToken, maxResults, dingUserIds);
                    } catch (Exception e) {
                        log.error("同步请假记录异常", e);
                    }
                }
            }



        }
    }

    private void getRecords(List<AttendanceLeaveRecordEntity> waitSyncLeaveRecords, ProcessInstanceEnum processInstanceEnum, long startTime, long endTime, Long nextToken, long maxResults, List<String> dingUserIds) throws Exception {
        Rest<ListProcessInstanceIdsResponseBody.ListProcessInstanceIdsResponseBodyResult> rest = dingPersonService.getProcessInstanceIdByUserIds(processInstanceEnum.getValue(), startTime, endTime, nextToken, maxResults, dingUserIds);
        if (YesNoEnum.YES.getValue() == rest.getCode()) {
            if (ObjectUtils.isEmpty(rest.getData().getNextToken())) {
                List<String> processInstanceList = rest.getData().getList();
                for (String process : processInstanceList) {
                    AttendanceLeaveRecordEntity leaveRecord = new AttendanceLeaveRecordEntity();
                    leaveRecord.setRemark(process);
                    //  leaveRecord.setStaffId(item.getCustomerId());
                    waitSyncLeaveRecords.add(leaveRecord);
                    log.info("更新记录成功==>" + process);
                }
            } else {
                nextToken = DataUtil.converStr2Long(rest.getData().getNextToken(), 0L);
                getRecords(waitSyncLeaveRecords, processInstanceEnum, startTime, endTime, nextToken, maxResults, dingUserIds);
            }
        }
    }

    private void getRecords(List<AttendanceLeaveRecordEntity> waitSyncLeaveRecords, QiongLaiProcessInstanceEnum processInstanceEnum, long startTime, long endTime, Long nextToken, long maxResults, List<String> dingUserIds) throws Exception {
        Rest<ListProcessInstanceIdsResponseBody.ListProcessInstanceIdsResponseBodyResult> rest = dingPersonService.getProcessInstanceIdByUserIds(processInstanceEnum.getValue(), startTime, endTime, nextToken, maxResults, dingUserIds);
        if (YesNoEnum.YES.getValue() == rest.getCode()) {
            if (ObjectUtils.isEmpty(rest.getData().getNextToken())) {
                List<String> processInstanceList = rest.getData().getList();
                for (String process : processInstanceList) {
                    AttendanceLeaveRecordEntity leaveRecord = new AttendanceLeaveRecordEntity();
                    leaveRecord.setRemark(process);
                    //  leaveRecord.setStaffId(item.getCustomerId());
                    waitSyncLeaveRecords.add(leaveRecord);
                    log.info("更新记录成功==>" + process);
                }
            } else {
                nextToken = DataUtil.converStr2Long(rest.getData().getNextToken(), 0L);
                getRecords(waitSyncLeaveRecords, processInstanceEnum, startTime, endTime, nextToken, maxResults, dingUserIds);
            }
        }
    }

    private void creatPerformAttend(AttendanceLeaveRecordEntity entity, Context context) {
        try {
            boolean leaveType = LeaveTypeEnum.事假.getValue() == entity.getLeaveType() || LeaveTypeEnum.病假.getValue() == entity.getLeaveType();
            if (AppealResultEnum.通过.getValue() == entity.getAuditResult() && leaveType) {
                //以半天4小时为计算单位进行扣分
                int multiple = entity.getDuration() / 14400;
                if (multiple > 0) {
                    String redisKey = entity.getLeavePersonId() + "_" + entity.getLeavePerson() + "_" + DateUtils.getDateTimeStr(entity.getStartTime(), "yyyyMMddHHmmss") + "_" + DateUtils.getDateTimeStr(entity.getEndTime(), "yyyyMMddHHmmss");
                    String leaveKey = cacheService.get(LEAVE_REIDS_KEY + redisKey);
                    if (StringUtils.isEmpty(leaveKey)) {
                        cacheService.set(LEAVE_REIDS_KEY + redisKey, redisKey, 300);     //五分钟内不会重复
                        PerformAttendRecordEntity recordEntity = conversionLeaveRecord(entity, multiple);
                        recordEntity.setCreateTime(new Date());
                        recordEntity.setCreateUserId(1l);
                        recordEntity.setErrorTime(entity.getStartTime());
//                        recordEntity.setScore(recordEntity.getScore().multiply(new BigDecimal(multiple)));
                        performAttendRecordService.save(recordEntity, context);
                    }
                }

                //汇总考勤绩效分数
                StaffCheckSummaryQuery checkSummaryQuery = new StaffCheckSummaryQuery();
                checkSummaryQuery.setStaffId(entity.getLeavePersonId());
                checkSummaryQuery.setCheckTimeStart(DateUtils.getStrDate(entity.getStartTime()));
                checkSummaryQuery.setCheckTimeEnd(checkSummaryQuery.getCheckTimeStart());
                checkSummaryQuery.setCheckType(CheckTypeEnum.考勤绩效.getValue());
                cacheService.lpush(KEY_CHECK_SUMMARY_CACHE, checkSummaryQuery);
            }
        } catch (Exception e) {
            log.error("创建假期考勤异常", e.getMessage());
        }

    }

    private PerformAttendRecordEntity conversionLeaveRecord(AttendanceLeaveRecordEntity entity, int multiple) {
        PerformAttendRecordEntity recordEntity = new PerformAttendRecordEntity();
        recordEntity.initAttrValue();
        StaffEntity staffEntity = staffService.get(entity.getLeavePersonId());
        recordEntity.setStaffId(entity.getLeavePersonId());
        recordEntity.setStaffName(entity.getLeavePerson());
        if (staffEntity != null) {
            recordEntity.setWorkNum(staffEntity.getWorkNum());
        }
        recordEntity.setDeptId(entity.getDeptId());
        recordEntity.setDeptName(entity.getDeptName());
        recordEntity.setSalaId(entity.getSalaId());
        recordEntity.setSalaName(entity.getSalaName());
        AttendanceGroupStaffEntity groupStaffEntity = attendanceGroupStaffService.selectOne(new AttendanceGroupStaffQuery().staffId(entity.getLeavePersonId()));
        if (groupStaffEntity != null) {
            recordEntity.setAttendanceGroupId(groupStaffEntity.getGroupId());
            recordEntity.setAttendanceGroupName(groupStaffEntity.getGroupName());
        }
        String ruleCode = "ATTEND1009";
        if (multiple % 2 > 0) {    //奇数，半天
            ruleCode = "ATTEND1010"; //事假半天
            if(entity.getLeaveType()==LeaveTypeEnum.病假.getValue()){
                ruleCode = "ATTEND1101"; //病假半天
            }
        }
        if (multiple % 2 == 0) {   //偶数，全天
            ruleCode = "ATTEND1011";    //事假全天
            if(entity.getLeaveType()==LeaveTypeEnum.病假.getValue()){
                ruleCode = "ATTEND1102";     //病假全天
            }
        }
        PerformRulesEntity performRulesEntity = performRulesService.selectOne(new PerformRulesQuery().ruleCode(ruleCode));
        if (performRulesEntity != null) {
            recordEntity.setRuleId(performRulesEntity.getId());
            recordEntity.setRuleName(performRulesEntity.getName());
            recordEntity.setCategoryId(performRulesEntity.getCategoryId());
            recordEntity.setCategoryName(performRulesEntity.getCategoryName());
            recordEntity.setSubAddType(performRulesEntity.getSubAddType());
            if (ruleCode.equals("ATTEND1011")||ruleCode.equals("ATTEND1102")) {
                int m = multiple / 2;
                recordEntity.setScore(performRulesEntity.getScore().multiply(new BigDecimal(m)));
            } else {
                recordEntity.setScore(performRulesEntity.getScore().multiply(new BigDecimal(multiple)));
            }
            recordEntity.setRemark(performRulesEntity.getContent());
        } else {
            throw new AppException("没有查询到请假扣分规则:" + ruleCode);
        }
        recordEntity.setSubMethod(SubMethodEnum.系统自动.getValue());
        recordEntity.setDeductPerson("系统管理员");
        return recordEntity;
    }

    @Override
    public void doCreatPerformAttend(Long id, Context context) {
        AttendanceLeaveRecordEntity temp = this.get(id);
        if (temp != null) {
            boolean leaveType = LeaveTypeEnum.事假.getValue() == temp.getLeaveType() || LeaveTypeEnum.病假.getValue() == temp.getLeaveType();
            if (AppealResultEnum.通过.getValue() == temp.getAuditResult() && leaveType) {
                creatPerformAttend(temp, context);
            }
        }
    }
}