package jnpf.workflow.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jnpf.attendance.antifreeze.UserAntifreeze;
import jnpf.attendance.mapper.AttendanceClockInResultMapper;
import jnpf.attendance.mapper.AttendanceDailyRuleMapper;
import jnpf.attendance.service.AttendanceClockInResultService;
import jnpf.attendance.service.AttendanceClockInService;
import jnpf.attendance.service.AttendanceDayStatisticsService;
import jnpf.attendance.service.AttendanceRepairService;
import jnpf.attendance.service.handle.notice.AttendanceNoticeHandler;
import jnpf.base.UserInfo;
import jnpf.base.service.SuperServiceImpl;
import jnpf.constants.AttendanceConstant;
import jnpf.engine.vo.ApproverByTaskIdAndNodeIdVo;
import jnpf.entity.attendance.AttendanceClockInResult;
import jnpf.entity.attendance.AttendanceRepair;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.entity.workflow.ApplyAttendanceRepair;
import jnpf.enums.attendance.ApprovalSettingTypeEnum;
import jnpf.enums.attendance.AttendanceNoticeEnum;
import jnpf.enums.attendance.ClockInStatusEnum;
import jnpf.exception.HandleException;
import jnpf.exception.QueryException;
import jnpf.model.attendance.vo.AttendanceRuleVo;
import jnpf.model.attendance.vo.GroupRuleVo;
import jnpf.model.attendance.vo.attendance.ApproveBaseImVo;
import jnpf.model.attendance.vo.attendance.ApproveImVo;
import jnpf.model.attendance.vo.attendance.AttendanceApproveImVo;
import jnpf.model.workflow.dto.ApplyAttendanceRepairDto;
import jnpf.model.workflow.vo.ApplyAttendanceRepairVo;
import jnpf.permission.model.user.PartUserInfoVo;
import jnpf.util.*;
import jnpf.workflow.mapper.ApplyAttendanceRepairMapper;
import jnpf.workflow.service.ApplyAttendanceRepairService;
import jnpf.workflow.service.FlowTaskService;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 补卡审批服务实现
 *
 * @author yanwenfu
 * @create 2023-12-13
 */
@Service
public class ApplyAttendanceRepairServiceImpl extends SuperServiceImpl<ApplyAttendanceRepairMapper, ApplyAttendanceRepair> implements ApplyAttendanceRepairService {

    @Resource
    private AttendanceClockInResultService attendanceClockInResultService;
    @Resource
    private AttendanceClockInResultMapper attendanceClockInResultMapper;
    @Resource
    private AttendanceClockInService attendanceClockInService;
    @Resource
    private AttendanceDayStatisticsService attendanceDayStatisticsService;
    @Resource
    private AttendanceDailyRuleMapper attendanceDailyRuleMapper;
    @Resource
    private AttendanceRepairService attendanceRepairService;
    @Resource
    private FlowTaskService flowTaskService;
    @Autowired
    private UserAntifreeze userAntifreeze;
    @Autowired
    private UserProvider userProvider;
    @Autowired
    private AttendanceNoticeHandler attendanceNoticeHandler;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private CustomTenantUtil customTenantUtil;

    @Override
    public ApplyAttendanceRepairVo getApplyAttendanceRepair(String id) {

        ApplyAttendanceRepair applyAttendanceRepair = this.getById(id);
        return JsonUtil.getJsonToBean(applyAttendanceRepair, ApplyAttendanceRepairVo.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(ApplyAttendanceRepair entity, ApplyAttendanceRepairDto applyAttendanceRepairDto) throws Exception {

        UserInfo userInfo = userProvider.get();
        // 提交前校验
        submitCheckRepair(entity.getClockInResultId());
        entity.setId(applyAttendanceRepairDto.getTaskId());
        entity.setApplyUser(userInfo.getUserId());
        entity.setApplyDate(new Date());
        entity.setTenantId(userInfo.getTenantId());
        setExtraValue(entity);
        save(entity);
        //sendToIm(entity, userInfo.getTenantId());
        // 表单信息
        /*if (StringUtil.isEmpty(entity.getId())) {
            entity.setId(id);
            save(entity);
            sendToIm(entity, userProvider.get().getTenantId());
        } else {
            entity.setId(id);
            updateById(entity);
        }*/
    }

    private void submitCheckRepair(String clockInResultId) throws Exception {

        // 判断当前出勤结果是否在进行别的审批
        int count = attendanceClockInResultMapper.getReplyingCount(clockInResultId);
        if (count > 0) {
            throw new QueryException("当前记录正在审批中，请审批结束后再重新执行操作");
        }
        AttendanceClockInResult clockInResult = attendanceClockInResultService.getById(clockInResultId);
        // 查询考勤规则
        AttendanceRuleVo rule = attendanceClockInService.getAttendanceRule(clockInResult.getRuleId());
        // 判定是否封账
        String monthDate = DateDetail.getDate2Str(rule.getDay(), DateDetail.DF15);
        Map<String, Boolean> map = attendanceDayStatisticsService.selectUserIsSeal(List.of(clockInResult.getUserId()), monthDate);
        if (map.get(clockInResult.getUserId())) {
            throw new HandleException(monthDate + "已封账，过去的考勤记录无法修改！");
        }
        // 用户补卡次数判定 (已使用次数 + 审批中的补卡)
        AttendanceRepair attendanceRepair = attendanceRepairService.getAttendanceRepair(rule.getUserId(), rule.getGroupId());
        String beginDate = String.format("%d-%02d-%02d", attendanceRepair.getGenerateYear(), attendanceRepair.getGenerateMonth(), attendanceRepair.getGenerateDay());
        String endDate = DateDetail.getDate2Str(attendanceRepair.getExpireDate(), DateDetail.DF);
        int applyCount = attendanceClockInResultMapper.selectRepairApplyCount(rule.getUserId(), rule.getGroupId(), beginDate, endDate);
        if (attendanceRepair.getRepairNum() - applyCount <= 0) {
            throw new QueryException("当前暂无补卡次数");
        }
    }

    private void setExtraValue(ApplyAttendanceRepair entity) {
        // 根据打卡结果id查询打卡结果
        AttendanceClockInResult clockInResult = attendanceClockInResultService.getById(entity.getClockInResultId());
        LambdaUpdateWrapper<AttendanceClockInResult> updateWrapper = new LambdaUpdateWrapper<AttendanceClockInResult>()
                .eq(AttendanceClockInResult::getId, clockInResult.getId())
                .set(AttendanceClockInResult::getApplyType, ConstantUtil.APPLY_REPAIR)
                .set(AttendanceClockInResult::getApplyId, entity.getId());
                // .set(AttendanceClockInResult::getAbsenceLeader, userProvider.get().getUserId());
        attendanceClockInResultService.update(updateWrapper);
        // 查询出勤规则
        FtbAttendanceDailyRule dailyRule = attendanceDailyRuleMapper.selectById(clockInResult.getRuleId());
        Date date;
        if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
            date = dailyRule.getInPoint();
        } else {
            date = dailyRule.getOutPoint();
        }
        entity.setRepairDateStr(DateDetail.getDate2Str(date, DateDetail.DF));
        entity.setRepairTimeStr(DateDetail.getDate2Str(date, DateDetail.DF10));
        entity.setClockInStatus(clockInResult.getClockInStatus());
        entity.setAbsence(clockInResult.getAbsence());
    }

    @Override
    public void submit(String id, ApplyAttendanceRepair entity, ApplyAttendanceRepairDto applyAttendanceRepairDto) {

        setExtraValue(entity);
        //表单信息
        if (StringUtil.isEmpty(entity.getId())) {
            entity.setId(id);
            save(entity);
            //sendToIm(entity, userProvider.get().getTenantId());
        } else {
            entity.setId(id);
            updateById(entity);
        }
    }

    private void sendToIm(ApplyAttendanceRepair entity, String tenantId) {

        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.schedule(() -> {
            customTenantUtil.checkOutTenant(tenantId);
            ApproverByTaskIdAndNodeIdVo data = flowTaskService.getApproveInfo(entity.getId(), null, tenantId,null);
            if (null == data) {
                return;
            }
            AttendanceApproveImVo approveIm = JsonUtil.getJsonToBean(data, AttendanceApproveImVo.class);
            approveIm.setId(data.getTaskId());
            List<PartUserInfoVo> userList = userAntifreeze.getAllByIds(List.of(entity.getCreatorUserId()), tenantId);
            PartUserInfoVo userInfo = userList.stream().findFirst().orElse(null);
            // 组装消息
            ApproveImVo approveImVo = new ApproveImVo(tenantId, AttendanceNoticeEnum.APPROVE);
            String content = "%s的【补卡】审批";
            String title = String.format(content, null != userInfo ? userInfo.getRealName() : "--");
            ApproveBaseImVo imDetail = new ApproveBaseImVo(title, ApprovalSettingTypeEnum.ROUTINE.getCode(), entity.getRepairDateStr() + " " + entity.getRepairTimeStr(), getResult(entity.getClockInStatus(), entity.getAbsence()));
            data.getUserOperators().forEach(v -> {
                approveIm.setTaskId(v.getTaskOperatorId());
                approveImVo.setUserIds(List.of(v.getUserId()));
                String encode = FtbUtil.encodeJson(approveIm);
                String url = AttendanceConstant.APPROVE_URL + encode;
                imDetail.setUrl(url);
                approveImVo.setApproveBaseImVo(imDetail);
                String key = ConstantUtil.APPROVE_NOTICE_REPAIR + approveIm.getId();
                redisUtil.insert(key, JSONUtil.toJsonStr(approveImVo));
                attendanceNoticeHandler.send(approveImVo);
            });
        }, 1, TimeUnit.SECONDS);
        scheduler.shutdown();
    }

    private String getResult(Integer clockInStatus, Integer absence) {

        if (null != absence && absence.equals(ConstantUtil.NUM_TRUE)) {
            return ClockInStatusEnum.ABSENCE.getDescription();
        } else {
            switch (clockInStatus) {
                case -1:
                    return ClockInStatusEnum.NO_CLOCK.getDescription();
                case 2:
                    return ClockInStatusEnum.WORK_LATE.getDescription();
                case 3:
                    return ClockInStatusEnum.HOME_EARLY.getDescription();
                default:
                    return "--";
            }
        }
    }
}
