/*
 * Copyright (c) 2023 codvision.com All Rights Reserved.
 */

package com.codvision.workflow.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codvision.auth.entity.vo.SysUserVO;
import com.codvision.auth.service.ISysUserService;
import com.codvision.auth.support.UserContext;
import com.codvision.base.support.QuartzJobManager;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.commoncore.utils.Preconditions;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.util.QueryPageUtil;
import com.codvision.workflow.entity.WorkFlowData;
import com.codvision.workflow.entity.WorkFlowDelayRecord;
import com.codvision.workflow.entity.WorkFlowRecord;
import com.codvision.workflow.entity.dto.WorkFlowDelayDTO;
import com.codvision.workflow.job.WorkFlowGlobalTimeOutJob;
import com.codvision.workflow.job.WorkFlowTaskCompleteTimeOutJob;
import com.codvision.workflow.mapper.WorkFlowDelayRecordMapper;
import com.codvision.workflow.service.WorkFlowDataService;
import com.codvision.workflow.service.WorkFlowDelayRecordService;
import com.codvision.workflow.service.WorkFlowRecordService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.codvision.workflow.constant.WorkFlowConstant.*;

/**
 * @author hxl
 */
@Service
@RequiredArgsConstructor
public class WorkFlowDelayRecordServiceImpl extends ServiceImpl<WorkFlowDelayRecordMapper, WorkFlowDelayRecord> implements WorkFlowDelayRecordService {

    private final TaskService taskService;
    private final ISysUserService sysUserService;
    private final QuartzJobManager quartzJobManager;
    private final WorkFlowRecordService workFlowRecordService;
    private final WorkFlowDataService workFlowDataService;
    @Override
    public IPage<WorkFlowDelayRecord> getPageResult(QueryPage<Map<String, Object>> queryPage) {
        SysUserVO sysUserVO = UserContext.get();
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        paraMap.put("uid", sysUserVO.getId());
        paraMap.put("oid", sysUserVO.getActiveOid());
        paraMap.put("roleId", sysUserVO.getActiveRoleId());
        return getBaseMapper().getPageResult(queryPage.getPage(), paraMap);
    }

    @Override
    public IPage<WorkFlowDelayRecord> myDelayRecords(QueryPage<Map<String, Object>> queryPage) {
        SysUserVO sysUserVO = UserContext.get();
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        paraMap.put("uid", sysUserVO.getId());
        return getBaseMapper().myDelayRecords(queryPage.getPage(), paraMap);
    }

    @Override
    public void add(WorkFlowDelayDTO workFlowDelayDTO) {
        SysUserVO sysUserVO = UserContext.get();
        Task task = taskService.createTaskQuery().taskId(workFlowDelayDTO.getTaskId())
                .or()
                .taskCandidateOrAssigned(String.valueOf(sysUserVO.getId()))
                .taskCandidateGroupIn(Collections.singletonList(String.valueOf(sysUserVO.getActiveOid())))
                .endOr()
                .singleResult();
        Preconditions.checkArgument(task != null,"获取任务信息失败");

        WorkFlowDelayRecord record = WorkFlowDelayRecord.builder()
                .instanceId(task.getProcessInstanceId()).newDeadline(workFlowDelayDTO.getDelayTime()).reason(workFlowDelayDTO.getReason()).status(0)
                .applicantUid(sysUserVO.getId()).applicantName(sysUserVO.getName()).applicantTel(sysUserVO.getMobile()).applicantOid(sysUserVO.getActiveOid())
                .build();

        if (workFlowDelayDTO.getDelayType() == null || workFlowDelayDTO.getDelayType() == 0) {
            WorkFlowRecord workFlowRecord = workFlowRecordService.getByTodoTaskId(workFlowDelayDTO.getTaskId());
            if (workFlowRecord == null) {
                return;
            }

            record.setType(0);
            record.setBizId(workFlowRecord.getId());
            record.setOldDeadline(workFlowRecord.getDeadlineTime());
        } else {
            WorkFlowData workFlowData = workFlowDataService.getFormDataByInstanceId(task.getProcessInstanceId());
            if (workFlowData == null) {
                return;
            }

            record.setType(1);
            record.setBizId(workFlowData.getId());
            record.setOldDeadline(workFlowData.getDeadlineTime());
        }

        WorkFlowDelayRecord workFlowDelayRecord = this.getOne(Wrappers.<WorkFlowDelayRecord>lambdaQuery()
                .eq(WorkFlowDelayRecord::getStatus, 0)
                .eq(WorkFlowDelayRecord::getBizId, record.getBizId()).eq(WorkFlowDelayRecord::getApplicantUid, sysUserVO.getId()));
        Preconditions.checkArgument(workFlowDelayRecord == null,"您已经有一条延期申请正在处理中，请勿重复申请");

        // 获取审核人员信息
        Integer uid = (Integer) taskService.getVariable(workFlowDelayDTO.getTaskId(), WORK_FLOW_DELAY_REVIEWER_CONFIG_USER);
        if (uid != null) {
            SysUserVO user = sysUserService.getDetail(uid);
            if (user != null) {
                record.setReviewerUid(uid);
                record.setReviewerOid(user.getActiveOid());
                record.setReviewerName(user.getName());
                record.setReviewerTel(user.getMobile());
            }
        } else {
            // 获取审核组织信息
            Integer oid = (Integer) taskService.getVariable(workFlowDelayDTO.getTaskId(), WORK_FLOW_DELAY_REVIEWER_CONFIG_ORG);
            if (oid != null) {
                record.setReviewerOid(oid);
            }

            Integer roleId = (Integer) taskService.getVariable(workFlowDelayDTO.getTaskId(), WORK_FLOW_DELAY_REVIEWER_CONFIG_ROLE);
            if (roleId != null) {
                record.setReviewerRoleId(roleId);
            }
        }

        this.save(record);
    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void audit(Integer id, Integer status, String comment) {
        WorkFlowDelayRecord record = this.getById(id);
        if (record == null) {
            throw new BusinessException("获取延期申请记录信息失败");
        }

        if (record.getStatus() != 0) {
            throw new BusinessException("当前申请已审核完毕");
        }

        // 添加审核状态、审核意见与审核时间
        record.setStatus(status);
        record.setComment(comment);
        record.setAuditTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN));
        // 添加当前审核人信息
        SysUserVO sysUserVO = UserContext.get();
        record.setReviewerUid(sysUserVO.getId());
        record.setReviewerOid(sysUserVO.getActiveOid());
        record.setReviewerName(sysUserVO.getName());
        record.setReviewerTel(sysUserVO.getMobile());
        this.updateById(record);

        if (status == 1) {
            String jobName;
            Class<?> clazz;
            Map<String, Object> paraMap = new HashMap<>(2);
            String cron = LocalDateTime.parse(record.getNewDeadline(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)).format(DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"));
            if (record.getType() == 0) {
                // 审核通过
                WorkFlowRecord workFlowRecord = workFlowRecordService.getById(record.getBizId());
                if (workFlowRecord == null) {
                    return;
                }

                // 设置新的截止时间
                workFlowRecord.setIsExpired(false);
                workFlowRecord.setDeadlineTime(record.getNewDeadline());
                workFlowRecordService.updateById(workFlowRecord);
                clazz = WorkFlowTaskCompleteTimeOutJob.class;
                paraMap.put("taskId", workFlowRecord.getTaskId());
                jobName = WORK_FLOW_TIME_OUT_COMPLETE_TASK_ID + workFlowRecord.getTaskId();
            } else {
                WorkFlowData workFlowData = workFlowDataService.getFormDataByInstanceId(record.getInstanceId());
                if (workFlowData == null) {
                    return;
                }

                workFlowData.setIsExpired(false);
                workFlowData.setDeadlineTime(record.getNewDeadline());
                workFlowDataService.updateById(workFlowData);
                clazz = WorkFlowGlobalTimeOutJob.class;
                paraMap.put("instanceId", record.getInstanceId());
                jobName = WORK_FLOW_TIME_OUT_INSTANCE_ID + record.getInstanceId();
            }

            // 清除原定时任务并添加新的任务
            quartzJobManager.deleteJob(jobName, WORK_FLOW_JOB_GROUP);
            quartzJobManager.addJob(clazz, jobName, WORK_FLOW_JOB_GROUP, cron, paraMap);
        }
    }
}
