package com.codvision.workflow.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.codvision.auth.entity.vo.SysUserVO;
import com.codvision.auth.service.ISysUserService;
import com.codvision.base.support.QuartzJobManager;
import com.codvision.workflow.constant.enums.WorkFlowFormInnerVariableEnum;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.TaskService;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.identitylink.api.IdentityLinkInfo;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.task.api.Task;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

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


/**
 * @author hxl
 */
@Service
@RequiredArgsConstructor
public class WorkFlowService {

    private final TaskService taskService;
    private final QuartzJobManager quartzJobManager;
    private final ISysUserService sysUserService;

    /**
     * 用户任务Job
     * @param delegateTask 当前任务
     * @param minutes 分钟
     * @param clazz 目标执行类
     * @param paraMap 参数
     */
    @SneakyThrows
    public void taskJob(DelegateTask delegateTask, String variableName, Integer minutes, Class<?> clazz, Map<String, Object> paraMap) {
        if (minutes == null) {
            return;
        }

        if (MapUtil.isEmpty(paraMap)) {
            paraMap = new HashMap<>(16);
        }

        // 拼接当前任务ID
        variableName = variableName + delegateTask.getId();
        // 计算日期偏移量
        DateTime offsetDatetime = DateUtil.offsetMinute(delegateTask.getCreateTime(), minutes);
        String deadlineTime = DateUtil.format(offsetDatetime, DatePattern.NORM_DATETIME_PATTERN);
        // 设置变量
        taskService.setVariableLocal(delegateTask.getId(), variableName, deadlineTime);
        // 日期转cron表达式
        String cron = LocalDateTime.parse(deadlineTime, DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN))
                .format(DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"));
        // 添加定时任务
        paraMap.put("taskId", delegateTask.getId());
        quartzJobManager.addJob(clazz, variableName, WORK_FLOW_JOB_GROUP, cron, paraMap);
    }

    /**
     * 获取任务关联的所有用户ID
     * @param taskId 任务ID
     * @return 用户ID列表
     */
    public List<Integer> getTaskRelatedUserIds(String taskId) {
        Set<Integer> userIds = new HashSet<>();
        Task task = taskService.createTaskQuery().taskId(taskId).includeIdentityLinks().singleResult();
        if (task == null) {
            return Collections.emptyList();
        }

        if (StringUtils.isNotBlank(task.getAssignee())) {
            userIds.add(Integer.parseInt(task.getAssignee()));
            return new ArrayList<>(userIds);
        }

        List<? extends IdentityLinkInfo> identityLinks = task.getIdentityLinks();
        if (CollectionUtils.isEmpty(identityLinks)) {
            return Collections.emptyList();
        }

        identityLinks = identityLinks.stream()
                .filter(identityLinkInfo -> IdentityLinkType.CANDIDATE.equals(identityLinkInfo.getType()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(identityLinks)) {
            return Collections.emptyList();
        }


        List<Integer> candidateUserIds = identityLinks.stream()
                .map(IdentityLinkInfo::getUserId).filter(StringUtils::isNotBlank).map(Integer::valueOf).distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(candidateUserIds)) {
            return candidateUserIds;
        }

        Optional<Integer> optional = identityLinks.stream()
                .map(IdentityLinkInfo::getGroupId).filter(StringUtils::isNotBlank).map(Integer::valueOf).findFirst();
        if (optional.isPresent()) {
            Map<String, Object> paraMap = new HashMap<>(2);
            paraMap.put("oid", optional.get());
            List<SysUserVO> sysUserVOList = sysUserService.listUsers(paraMap);
            if (CollectionUtils.isNotEmpty(sysUserVOList)) {
                userIds.addAll(sysUserVOList.stream().map(SysUserVO::getId).collect(Collectors.toSet()));
            }
        }

        return new ArrayList<>(userIds);
    }

    /**
     * 清除当前节点办理人、候选人或者候选组信息
     * @param taskId
     */
    @SuppressWarnings("all")
    public void clearCandidates(String taskId) {
        taskService.setAssignee(taskId, null);
        List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
        if (CollectionUtils.isEmpty(identityLinks)) {
            return;
        }

        for (int i = 0; i < identityLinks.size(); i++) {
            IdentityLink identityLink = identityLinks.get(i);
            if (StringUtils.isNotBlank(identityLink.getUserId())) {
                taskService.deleteCandidateUser(taskId, identityLink.getUserId());
            }

            if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                taskService.deleteCandidateGroup(taskId, identityLink.getGroupId());
            }
        }
    }


    /**
     * 表单内置变量初始化
     * @param formMap 表单参数
     */
    public void initDefaultVariables(Map<String, Object> formMap) {
        if (MapUtil.isEmpty(formMap)) {
            return;
        }

        for (Map.Entry<String, Object> entry : formMap.entrySet()) {
            if (entry.getValue() instanceof String) {
                String value = (String) entry.getValue();
                if (!WorkFlowFormInnerVariableEnum.isInner(value)) {
                    continue;
                }

                value = WorkFlowFormInnerVariableEnum.get(value);
                value = value == null ? "" : value;
                entry.setValue(value);
            }
        }
    }
}
