package cn.axzo.workflow.core.engine.job;

import cn.axzo.basics.common.util.NumberUtil;
import cn.axzo.workflow.common.model.dto.AlterDTO;
import cn.axzo.workflow.common.model.dto.TermNodePausingDTO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.listener.Alter;
import cn.axzo.workflow.core.service.CategoryService;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.task.api.Task;
import org.flowable.variable.api.delegate.VariableScope;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER;

/**
 * 检查指定节点是否长时间卡住，如果卡住则进行钉钉告警
 *
 * @author wangli
 * @since 2024-09-11 13:50
 */
@Slf4j
public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements JobHandler {
    public static final String TYPE = "term-node-alter-cycle";
    private final SupportRefreshProperties refreshProperties;

    public AsyncTermNodeAlterJobHandler(SupportRefreshProperties refreshProperties) {
        this.refreshProperties = refreshProperties;
    }

    @Override
    public String getType() {
        return TYPE;
    }

    @Override
    public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
        log.warn("AsyncTermNodeAlterJobHandler exec start...");
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
//        JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration());
//        if (!jsonObject.containsKey("activityId")) {
//            return;
//        }
        String activityId = job.getJobHandlerConfiguration();
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();
        if(Objects.isNull(processInstance)) {
            return;
        }
        TermNodePausingDTO dto = runtimeService.getVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, TermNodePausingDTO.class);
        TaskService taskService = processEngineConfiguration.getTaskService();
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(dto.getProcessInstanceId())
                .taskDefinitionKey(dto.getActivityId())
                .active()
                .list();

        StringBuilder sb = new StringBuilder();
        tasks.forEach(e -> {
            sb.append("id：").append(e.getId()).append(", assignee: ").append(e.getAssignee());
        });
        log.info("tasks size:{}, info: {}", tasks.size(), JSON.toJSONString(sb));
        if (CollectionUtils.isEmpty(tasks) || tasks.size() > 1 || hasAssignee(tasks.get(0).getAssignee())) {
            return;
        }

        if (DateUtil.compare(DateUtil.date(), getDateTime(tasks.get(0).getCreateTime())) <= 0) {
            ManagementService managementService = processEngineConfiguration.getManagementService();
            managementService.executeCommand(context -> {
                TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
                TimerJobEntity timerJobEntity = timerJobService.createTimerJob();
                timerJobEntity.setJobType("timer");
                timerJobEntity.setJobHandlerType(AsyncTermNodeAlterJobHandler.TYPE); // 这里填写你自定义的 JobHandler 类型
                timerJobEntity.setProcessInstanceId(dto.getProcessInstanceId());
                timerJobEntity.setExecutionId(null);
                timerJobEntity.setDuedate(getDateTime(new Date())); // 立即执行
                timerJobEntity.setRepeat(null); // 不重复
                timerJobEntity.setRetries(1);
                timerJobEntity.setJobHandlerConfiguration(dto.getActivityId()); // 可选，传递参数
                timerJobService.scheduleTimerJob(timerJobEntity);
                return null;
            });
            return;
        }

        // 不允许重复告警
        if (!refreshProperties.getRepeatAlter() && dto.getRetries() > 0) {
            return;
        }
        // 超过告警次数
        if (refreshProperties.getAlterRetries() != 0 && dto.getRetries() >= refreshProperties.getAlterRetries()) {
            return;
        }

        CategoryService bean = SpringContextUtils.getBean(CategoryService.class);
        Optional<CategoryItemVO> bpmModelCategory = bean.get("bpm_model_category", processInstance.getProcessDefinitionKey());
        // 发送告警对象
        Alter alter = SpringContextUtils.getBean(Alter.class);
        AlterDTO alterDTO = new AlterDTO();
        alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
        alterDTO.setProcessDefinitionName(bpmModelCategory.orElse(new CategoryItemVO()).getLabel());
        alterDTO.setProcessInstanceId(dto.getProcessInstanceId());
        alterDTO.setActivityId(dto.getActivityId());
        alterDTO.setTaskId(tasks.get(0).getId());
        alterDTO.setStartTime(tasks.get(0).getCreateTime());
        alterDTO.setPrettyStartTime(DateUtil.formatDateTime(tasks.get(0).getCreateTime()));
        if (Boolean.TRUE.equals(refreshProperties.getAlterSendDingTalk())) {
            alter.invoke(alterDTO);

            // 记录告警次数
            incRetries(job, dto, runtimeService, activityId);

        }
    }

    private DateTime getDateTime(Date date) {
        DateTime dateTime;
        switch (refreshProperties.getAlterIntervalUnit()) {
            case MINUTES:
                dateTime = DateUtil.offsetMinute(date, refreshProperties.getPauseDelay());
                break;
            case HOURS:
                dateTime = DateUtil.offsetHour(date, refreshProperties.getPauseDelay());
                break;
            default:
                dateTime = DateUtil.offsetSecond(date, refreshProperties.getPauseDelay());
                break;
        }
        return dateTime;
    }

    private void incRetries(JobEntity job, TermNodePausingDTO dto, RuntimeService runtimeService, String activityId) {
        dto.setRetries(dto.getRetries() + 1);
        runtimeService.setVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, dto);
    }

//    private void deleteTimerJob(TermNodePausingDTO dto) {
//        Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
//                new DeleteTimerJobTransactionListener(dto));
//    }

    private Boolean hasAssignee(String assignee) {
        if (!StringUtils.hasText(assignee)) {
            return false;
        }
        String[] split = assignee.split("\\|");
        return split.length == 2 && NumberUtil.isPositiveNumber(Long.valueOf(split[1]));
    }


}
