package com.ruoyi.project.business.service.flow.impl;

import static com.ruoyi.project.business.enums.AuditStatusEnum.DRAFT;
import static com.ruoyi.project.business.enums.AuditStatusEnum.INIT;
import static com.ruoyi.project.business.enums.AuditStatusEnum.PASS;
import static com.ruoyi.project.business.enums.AuditStatusEnum.REJECT;
import static com.ruoyi.project.business.enums.AuditStatusEnum.ROLLBACK;
import static com.ruoyi.project.business.enums.AuditStatusEnum.isAuditing;
import static com.ruoyi.project.business.enums.SignTypeEnum.COUNTER_SIGN;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ruoyi.common.exception.ServiceErrorException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.email.BaseEmailEntity;
import com.ruoyi.common.utils.email.EmailUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.project.business.domain.TbAgent;
import com.ruoyi.project.business.domain.TbArea;
import com.ruoyi.project.business.domain.TqBargainPrice;
import com.ruoyi.project.business.domain.TqQuotation;
import com.ruoyi.project.business.domain.flow.Flow;
import com.ruoyi.project.business.domain.flow.FlowAuditParam;
import com.ruoyi.project.business.domain.flow.FlowConfig;
import com.ruoyi.project.business.domain.flow.FlowTask;
import com.ruoyi.project.business.domain.flow.FlowTaskUser;
import com.ruoyi.project.business.domain.flow.FlowTodoListParam;
import com.ruoyi.project.business.domain.flow.FlowVO;
import com.ruoyi.project.business.domain.flow.NodeConfig;
import com.ruoyi.project.business.domain.system.SysAttachment;
import com.ruoyi.project.business.domain.vo.quotation.QuotationPriceVO;
import com.ruoyi.project.business.dto.TaskGenerateParams;
import com.ruoyi.project.business.enums.*;
import com.ruoyi.project.business.mapper.flow.FlowConfigMapper;
import com.ruoyi.project.business.mapper.flow.FlowMapper;
import com.ruoyi.project.business.mapper.flow.FlowTaskMapper;
import com.ruoyi.project.business.mapper.flow.FlowTaskUserMapper;
import com.ruoyi.project.business.service.ITbAgentService;
import com.ruoyi.project.business.service.ITbAreaService;
import com.ruoyi.project.business.service.flow.IFlowBusinessService;
import com.ruoyi.project.business.service.flow.IFlowService;
import com.ruoyi.project.business.service.quotation.ITqBargainPriceService;
import com.ruoyi.project.business.service.quotation.ITqQuotationService;
import com.ruoyi.project.business.service.system.ISysAttachmentService;
import com.ruoyi.project.common.domain.Message;
import com.ruoyi.project.common.service.ISseEmitterService;
import com.ruoyi.project.system.domain.SysRole;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.service.ISysRoleService;
import com.ruoyi.project.system.service.ISysUserService;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class FlowServiceImpl implements IFlowService {

    private static final Logger log = LoggerFactory.getLogger(FlowServiceImpl.class);

    public static final DecimalFormat formatter = new DecimalFormat("#,###");

    private ScriptEngineManager manager = new ScriptEngineManager();

    @Resource
    private EmailUtil emailUtil;

    @Resource
    private FlowConfigMapper flowConfigMapper;

    @Resource
    private FlowMapper flowMapper;

    @Resource
    private FlowTaskMapper flowTaskMapper;

    @Resource
    private FlowTaskUserMapper flowTaskUserMapper;

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private ISysRoleService sysRoleService;

    @Resource
    private IFlowBusinessService flowBusinessService;

    @Resource
    private ISysAttachmentService sysAttachmentService;
    @Resource
    @Lazy
    private ITqQuotationService tqQuotationService;
    @Resource
    private ITqBargainPriceService tqBargainPriceService;

    @Resource
    private ITbAreaService tbAreaService;

    @Resource
    private ITbAgentService tbAgentService;

    @Resource
    private ISseEmitterService sseEmitterService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rollback(FlowAuditParam auditParam) {
        Flow flow = flowMapper.getBusinessFlow(auditParam.getAuditFlow(), auditParam.getBusinessId());
        auditParam.setCurrentStatus(flow.getStatus());
        if (Objects.isNull(flow)) {
            throw new ServiceErrorException("业务数据状态已变更，请刷新后重试");
        }
        flow.setStatus(ROLLBACK.getValue());
        flow.setUpdateTime(new Date());
        flow.setUpdateBy(SecurityUtils.getUserId().toString());
        flowMapper.updateStatus(flow);
        auditParam.setStatus(ROLLBACK.getValue());
        auditParam.setFinish(true);
        auditParam.setPromoter(flow.getPromoter());
        auditParam.setBusinessData(flow.getBusinessData());
        flowBusinessService.changeBusinessStatus(auditParam);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean auditBatch(FlowAuditParam auditParam) {
        List<Long> businessIds = auditParam.getBusinessIds();
        if (CollectionUtils.isEmpty(businessIds)) {
            throw new ServiceErrorException("请选择审批数据");
        }
        businessIds.forEach(businessId -> {
            FlowAuditParam param = new FlowAuditParam();
            BeanUtils.copyProperties(auditParam, param);
            param.setBusinessId(businessId);
            this.audit(param);
        });
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean audit(FlowAuditParam auditParam) {
        AuditStatusEnum auditStatus = AuditStatusEnum.from(auditParam.getStatus());
        Flow flow = flowMapper.getBusinessFlow(auditParam.getAuditFlow(), auditParam.getBusinessId());
        if (Objects.isNull(flow)) {
            throw new ServiceErrorException("数据异常，审批流程数据不存在");
        }
        auditParam.setCurrentStatus(flow.getStatus());
        auditParam.setBusinessData(flow.getBusinessData());
        if (StringUtils.isBlank(auditParam.getRemark())) {
            auditParam.setRemark("无");
        } else {
            auditParam.setRemark(auditParam.getRemark()
                    .replaceAll("undefined", "无")
                    .replaceAll("null", "无"));
        }
        if (Objects.isNull(flow) || !AuditStatusEnum.isAuditing(flow.getStatus())) {
            throw new ServiceErrorException("业务数据状态已变更，请刷新后重试");
        }
        auditParam.setPromoter(flow.getPromoter());
        LoginUser currentUser = SecurityUtils.getLoginUser();
        FlowTask currentTask = flowTaskMapper.getFlowTasks(flow.getId()).stream()
            .filter(flowTask -> flowTask.getStatus().equals(INIT.getValue())).findFirst().get();
        List<FlowTaskUser> currentTaskUsers = flowTaskUserMapper.listTaskUsers(Lists.newArrayList(currentTask.getId()))
            .stream().filter(user -> user.getStatus().equals(INIT.getValue())).collect(toList());

        // 1. 记录用户审批结果
        FlowTaskUser param = new FlowTaskUser();
        param.setStatus(auditParam.getStatus());
        param.setRemark(auditParam.getRemark());
        param.setAuditor(SecurityUtils.getUserId().toString());
        param.setAuditTime(new Date());
        param.setUpdateTime(new Date());
        param.setUpdateBy(SecurityUtils.getUserId().toString());

        if (SecurityUtils.isSuperAdmin()){
            param.setIds(currentTaskUsers.stream().filter(flowTaskUser -> isAuditing(flowTaskUser.getStatus()))
                    .map(FlowTaskUser::getId).collect(toList()).subList(0, 1));
        } else {
            param.setIds(currentTaskUsers.stream().filter(flowTaskUser ->
                            flowTaskUser.getAuditUser().equals(currentUser.getUserId().toString()) && isAuditing(
                                    flowTaskUser.getStatus()))
                    .map(FlowTaskUser::getId).collect(toList()));
        }


        if (CollectionUtils.isEmpty(param.getIds())) {
            throw new ServiceErrorException("当前用户不具备审批权限");
        }

        flowTaskUserMapper.updateStatusBatch(param);
        //        sysAttachmentService.updateAttachments(param.getIds().get(0).intValue(),
        //                ModuleEnum.from(auditParam.getModule()),
        //                FunctionEnum.from(auditParam.getFunc()),
        //                CategoryEnum.from(auditParam.getCategory()),
        //                auditParam.getAttachmentList());

        // 如果是驳回直接中断审批
        if (REJECT.equals(auditStatus)) {
            currentTask.setStatus(REJECT.getValue());
            currentTask.setUpdateTime(new Date());
            currentTask.setUpdateBy(currentUser.getUsername());
            flowTaskMapper.updateStatus(currentTask);
            flow.setStatus(REJECT.getValue());
            flow.setUpdateTime(new Date());
            flow.setUpdateBy(currentUser.getUsername());
            flowMapper.updateStatus(flow);
            auditParam.setFinish(true);
            flowBusinessService.changeBusinessStatus(auditParam);
            return true;
        }

        // 会签存在人未审批则本次处理结束
        SignTypeEnum signType = SignTypeEnum.from(currentTask.getSignType());
        if (COUNTER_SIGN.equals(signType) &&
            flowTaskUserMapper.listTaskUsers(Lists.newArrayList(currentTask.getId()))
                .stream().filter(user -> user.getStatus().equals(INIT.getValue())).count() > 0) {
            return true;
        }

        // 当前节点审批通过
        currentTask.setStatus(AuditStatusEnum.PASS.getValue());
        currentTask.setUpdateBy(SecurityUtils.getUserId().toString());
        currentTask.setUpdateTime(new Date());
        flowTaskMapper.updateStatus(currentTask);

        // 无后置节点则流程结束
        String nodeId = currentTask.getNextTasks();
        if (isBlank(nodeId)) {
            flow.setStatus(AuditStatusEnum.PASS.getValue());
            flow.setUpdateTime(new Date());
            flow.setUpdateBy(currentUser.getUsername());
            flowMapper.updateStatus(flow);
            auditParam.setFinish(true);
            auditParam.setBusinessData(flow.getBusinessData());
            flowBusinessService.changeBusinessStatus(auditParam);
            return true;
        }

        // 激活后置节点
        FlowTask nextTask = flowTaskMapper.getNext(flow.getId(), nodeId);
        nextTask.setStatus(INIT.getValue());
        nextTask.setUpdateTime(new Date());
        nextTask.setUpdateBy(currentUser.getUsername());
        flowTaskMapper.updateStatus(nextTask);

        flow.setStatus(nextTask.getStatusCode());
        flow.setUpdateTime(new Date());
        flow.setUpdateBy(currentUser.getUsername());
        flowMapper.updateStatus(flow);

        List<FlowTaskUser> nextTaskUsers = flowTaskUserMapper.listTaskUsers(Lists.newArrayList(nextTask.getId()));
        FlowTaskUser nextParam = new FlowTaskUser();
        nextParam.setIds(nextTaskUsers.stream().map(FlowTaskUser::getId).collect(toList()));
        nextParam.setStatus(INIT.getValue());
        nextParam.setUpdateTime(new Date());
        nextParam.setUpdateBy(currentUser.getUsername());
        flowTaskUserMapper.updateStatusBatch(nextParam);

        auditParam.setStatus(nextTask.getStatusCode());
        auditParam.setNextAuditUsers(
            nextTaskUsers.stream().map(FlowTaskUser::getAuditUser).map(Long::valueOf).collect(toList()));
        flowBusinessService.changeBusinessStatus(auditParam);

        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean generateTasks(TaskGenerateParams taskGenerateParams) {
        AuditFlowEnum auditFlow = taskGenerateParams.getAuditFlow();
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(taskGenerateParams.getBusinessData()));
        if (Objects.isNull(jsonObject.get("id")) || Objects.isNull(taskGenerateParams.getUserId())) {
            throw new RuntimeException("业务数据异常，缺少业务数据id或申请人id");
        }

        Long businessId = Long.parseLong(jsonObject.get("id").toString());
        Long userId = taskGenerateParams.getUserId();
        Long areaId =Long.parseLong(jsonObject.get("areaId").toString());
        Long agentId =Long.parseLong(jsonObject.get("agentId").toString());
        if (Objects.isNull(areaId)) {
            throw new RuntimeException("所属区域不能为空");
        }

        Flow exist = getFlowMap(Lists.newArrayList(businessId), auditFlow).get(businessId);
        if (Objects.nonNull(exist) && AuditStatusEnum.isAuditing(exist.getStatus())) {
            throw new ServiceErrorException("已存在进行中的审批，请勿重复提交");
        }
        // 删除当前业务下的所有任务
        remove(auditFlow, Lists.newArrayList(businessId));

        Flow flow = new Flow();
        flow.setBusinessId(businessId);
        flow.setBusinessDesc(taskGenerateParams.getBusinessDesc());
        flow.setAuditFlow(auditFlow.getValue());
        flow.setPromoter(userId);
        flow.setBusinessData(jsonObject.toString());
        flow.setCreateTime(new Date());
        flow.setCreateBy(SecurityUtils.getUserId().toString());

        // 1. 根据审批数据直接所属公司及流程类型获取审批流程图
        FlowConfig param = new FlowConfig();
        param.setCompanyId(1L);
        param.setAuditFlow(auditFlow.getValue());
        FlowConfig flowConfig = getConfig(param);
        NodeConfig nodeConfig = null;
        List<NodeConfig> auditors = null;
        if (Objects.nonNull(flowConfig)) {
            nodeConfig = JSON.parseObject(flowConfig.getFlowData(), NodeConfig.class);
            auditors = extractAuditors(flow, nodeConfig);
        }
        if (Objects.isNull(nodeConfig) || CollectionUtils.isEmpty(auditors)) {
            flow.setFlowConfigId(-1L);
            flow.setStatus(AuditStatusEnum.PASS.getValue());
            flow.setAuditFlow(auditFlow.getValue());
            flowMapper.save(flow);
            FlowAuditParam auditParam = new FlowAuditParam();
            auditParam.setStatus(AuditStatusEnum.PASS.getValue());
            auditParam.setBusinessId(flow.getBusinessId());
            auditParam.setBusinessData(flow.getBusinessData());
            auditParam.setAuditFlow((long)flow.getAuditFlow());
            auditParam.setFinish(true);
            auditParam.setCurrentStatus(INIT.getValue());
            flowBusinessService.changeBusinessStatus(auditParam);
            return false;
        } else {
            //审核中
            flow.setStatus(INIT.getValue());
        }
        // 2. 生成并保存流程及其任务节点信息
        flow.setFlowConfigId(flowConfig.getId());
        flow.setAuditFlow(flowConfig.getAuditFlow());
        flowMapper.save(flow);
        // 3. 根据条件生成审批流程(路径中没有审批人则直接通过)
        Long flowId = flow.getId();
        if (CollectionUtils.isEmpty(auditors)) {
            Flow auditParam = new Flow();
            auditParam.setId(flowId);
            auditParam.setStatus(AuditStatusEnum.PASS.getValue());
            flowMapper.updateStatus(auditParam);
            FlowAuditParam params = new FlowAuditParam();
            params.setStatus(AuditStatusEnum.PASS.getValue());
            params.setBusinessId(flow.getBusinessId());
            params.setBusinessData(flow.getBusinessData());
            params.setAuditFlow((long)flow.getAuditFlow());
            params.setFinish(true);
            params.setCurrentStatus(INIT.getValue());
            flowBusinessService.changeBusinessStatus(params);
            return false;
        }
        // 3.1 设置前后置关系及状态
        NodeConfig prevNode = null;
        for (NodeConfig node : auditors) {
            if (Objects.nonNull(prevNode)) {
                node.setPrevTasks(prevNode.getNodeId());
                prevNode.setNextTasks(node.getNodeId());
                node.setStatus(DRAFT.getValue());
            } else {
                node.setStatus(INIT.getValue());
            }
            prevNode = node;
        }
        // 3.2 以审批人为单位生成任务
        List<FlowTask> flowTaskList = Lists.newArrayList();
        List<FlowTaskUser> flowTaskUserList = Lists.newArrayList();
        List<Long> roleIds = auditors.stream()
            .map(NodeConfig::getRoles)
            .flatMap(Collection::stream)
            .map(SysRole::getRoleId).distinct().collect(toList());
        Map<Long, List<Long>> roleUserMap = sysRoleService.getRoleUserMap(roleIds);
        SysUser sysUser = new SysUser();
        sysUser.setUserIdList(roleUserMap.values().stream().flatMap(Collection::stream).distinct().collect(toList()));
        // 有当前区域权限（当前区域或者上级区域）的用户
        TbArea tbArea = tbAreaService.selectTbAreaByAreaId(areaId);
        List<Long> parentAreaIds = Arrays.stream(tbArea.getPermissionChar().split(","))
                .filter(StringUtils::isNotBlank).map(Long::parseLong).collect(toList());
        sysUser.setAreaId(parentAreaIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        List<Long> areaUserIdList = sysUserService.selectUserList(sysUser).stream().map(SysUser::getUserId).collect(toList());

        for (NodeConfig node : auditors) {
            FlowTask flowTask = new FlowTask();
            BeanUtils.copyProperties(node, flowTask);
            flowTask.setFlowId(flowId);
            flowTask.setNodeName(node.getNodeType().getLabel());
            flowTask.setStatusCode(Integer.valueOf(node.getNodeType().getValue()));
            flowTask.setSignType(Integer.valueOf(node.getSignType().getValue()));
            flowTask.setAuditorType(Integer.valueOf(node.getAuditorType().getValue()));
            flowTaskList.add(flowTask);
        }
        flowTaskMapper.saveBatch(flowTaskList);

        Map<String, Long> idMap = flowTaskList.stream().collect(toMap(FlowTask::getNodeId, FlowTask::getId));
        for (NodeConfig node : auditors) {
            AuditorTypeEnum auditorType = AuditorTypeEnum.from(Integer.valueOf(node.getAuditorType().getValue()));
            List<Long> userIds = Lists.newArrayList();
            switch (auditorType) {
                case USER:
                    userIds = node.getUsers().stream()
                        .map(SysUser::getUserId).collect(toList());
                    break;
                case ROLE:
                    userIds = node.getRoles().stream()
                        .map(sysRole -> roleUserMap.getOrDefault(sysRole.getRoleId(), new ArrayList<>()))
                        .flatMap(Collection::stream).collect(toList());
                    // 只保留有区域权限的用户
                    userIds.retainAll(areaUserIdList);
                    break;
                case SUPERIOR_SALES:
                    // 取当前数据代理商的上级销售
                    TbAgent agent = tbAgentService.getById(agentId);
                    if (Objects.isNull(agentId) || Objects.isNull(agent) ) {
                        throw new RuntimeException("代理商信息不能为空");
                    }
                    userIds = Lists.newArrayList(agent.getParentAgentId());
                    break;
            }
            // 将申请人从审批人中移除
//            userIds.remove(SecurityUtils.getUserId());
            if (CollectionUtils.isEmpty(userIds)) {
                throw new ServiceException("节点不存在可用的审批人：" + node.getNodeName());
            }
            flowTaskUserList.addAll(userIds.stream().distinct().map(id -> {
                FlowTaskUser flowTaskUser = new FlowTaskUser();
                flowTaskUser.setTaskId(idMap.get(node.getNodeId()));
                flowTaskUser.setStatus(node.getStatus());
                flowTaskUser.setAuditUser(id.toString());
                flowTaskUser.setCreateTime(new Date());
                return flowTaskUser;
            }).collect(toList()));
        }
        if (CollectionUtils.isNotEmpty(flowTaskUserList)) {
            flowTaskUserMapper.saveBatch(flowTaskUserList);
        }
        List<Long> auditorUserIds = flowTaskUserList.stream()
            .filter(flowtaskUser -> INIT.getValue().equals(flowtaskUser.getStatus()))
            .map(FlowTaskUser::getAuditUser).map(Long::valueOf).collect(toList());

        FlowAuditParam params = new FlowAuditParam();
        params.setStatus(flowTaskList.get(0).getStatusCode());
        params.setBusinessId(flow.getBusinessId());
        params.setBusinessData(flow.getBusinessData());
        params.setAuditFlow((long)flow.getAuditFlow());
        params.setNextAuditUsers(auditorUserIds);
        params.setCurrentStatus(INIT.getValue());
        flowBusinessService.changeBusinessStatus(params);
        flow.setStatus(params.getStatus());
        flowMapper.updateStatus(flow);
        return true;
    }

    private List<NodeConfig> extractAuditors(Flow flow, NodeConfig nodeConfig) {
        List<NodeConfig> auditNodes = Lists.newArrayList();
        ScriptEngine scriptEngine = manager.getEngineByName("javascript");
        scriptEngine.put("promoter", sysUserService.selectUserById(flow.getPromoter()));
        scriptEngine.put("data", JSON.parseObject(flow.getBusinessData()));

        NodeConfig nextNode = nodeConfig.getChildNode();
        while (Objects.nonNull(nextNode)) {
            NodeTypeEnum nodeType = NodeTypeEnum.from(nextNode.getType());
            NodeConfig child = clone(nextNode.getChildNode());
            NodeConfig parent = clone(nextNode.getParent());

            switch (nodeType) {
                case ROUTER:
                    List<NodeConfig> conditionNodes = nextNode.getConditionNodes();
                    try {
                        // 根据条件选择进入哪条子流程
                        for (NodeConfig condition : conditionNodes) {
                            String expression = condition.getExpression();
                            if (StringUtils.isBlank(expression)) {
                                expression = "true";
                            }
                            Boolean result = (Boolean)scriptEngine.eval("Boolean(" + expression + ")");
                            if (result) {
                                NodeConfig routeStart = clone(condition);
                                routeStart.setParent(nextNode);
                                nextNode = routeStart;
                                break;
                            }
                        }
                        break;
                    } catch (ScriptException e) {
                        e.printStackTrace();
                        throw new ServiceErrorException("条件执行异常");
                    }
                case CONDITION:
                    if (Objects.nonNull(child)) {
                        child.setParent(parent);
                        nextNode = child;
                    } else if (Objects.nonNull(parent)) {
                        nextNode = parent.getChildNode();
                        if (Objects.nonNull(nextNode)) {
                            nextNode.setParent(parent.getParent());
                        }
                    } else {
                        nextNode = null;
                    }
                    break;
                case AUDITOR:
                    auditNodes.add(nextNode);
                    if (Objects.nonNull(child)) {
                        child.setParent(parent);
                        nextNode = child;
                    } else if (Objects.nonNull(parent)) {
                        // 如果是子流程结束需要回到父流程继续
                        nextNode = parent.getChildNode();
                    } else {
                        nextNode = null;
                    }
                    break;
            }
        }

        return auditNodes;
    }

    private NodeConfig clone(NodeConfig nodeConfig) {
        if (Objects.isNull(nodeConfig)) {
            return null;
        }
        return JSON.parseObject(JSON.toJSONString(nodeConfig), NodeConfig.class);
    }

    @Override
    public int remove(AuditFlowEnum auditFlow, List<Long> businessIds) {
        return flowMapper.remove(auditFlow, businessIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int saveConfig(FlowConfig flowConfig) {
        FlowConfig exist = getConfig(flowConfig);
        if (Objects.nonNull(exist)) {
            flowConfigMapper.remove(flowConfig);
        }
        return flowConfigMapper.save(flowConfig);
    }

    @Override
    public FlowConfig getConfig(FlowConfig flowConfig) {
        return flowConfigMapper.get(flowConfig.getCompanyId(), flowConfig.getAuditFlow());
    }

    @Override
    public int removeConfig(FlowConfig flowConfig) {
        return flowConfigMapper.remove(flowConfig);
    }

    @Override
    public Map<Long, Flow> getFlowMap(List<Long> ids, AuditFlowEnum auditFlow) {
        return getFlowMap(ids, Lists.newArrayList(auditFlow));
    }

    @Override
    public Map<Long, Flow> getFlowMap(List<Long> ids, List<AuditFlowEnum> auditFlows) {
        if (CollectionUtils.isEmpty(ids)) {
            return Maps.newHashMap();
        }
        List<Flow> flowList = flowMapper.getFlowList(ids, auditFlows, INIT.getValue());
        if (CollectionUtils.isEmpty(flowList)) {
            return Maps.newHashMap();
        }
        List<Long> taskIds = flowList.stream().map(Flow::getCurrentTaskId).collect(toList());
        Map<Long, Flow> result = Maps.newHashMap();
        Map<Long, List<FlowTaskUser>> flowUserMap = flowTaskUserMapper.listTaskUsers(taskIds).stream()
            .collect(groupingBy(FlowTaskUser::getTaskId));

        flowList.forEach(flow -> {
            flow.setCurrentAuditors(flowUserMap.get(flow.getCurrentTaskId()));
            Long key = flow.getBusinessId();
            Flow exist = result.get(key);
            if (Objects.isNull(exist) || exist.getCreateTime().before(flow.getCreateTime())) {
                result.put(key, flow);
            }
        });

        return result;
    }

    @Override
    public List<FlowVO> getTodoList(FlowTodoListParam params) {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        params.setUserId(sysUser.getUserId());
        List<FlowVO> result = flowMapper.getTodoList(params);
        result.forEach(flowVO -> {
            flowVO.setAuditFlowName(
                DictUtils.getInternationalDictLabel("audit_flow", flowVO.getAuditFlow().toString()));
        });
        return result;
    }

    @Override
    public List<FlowTaskUser> getAuditHistory(List<AuditFlowEnum> auditFlows, Integer businessId) {
        if (CollectionUtils.isEmpty(auditFlows)) {
            throw new ServiceErrorException("流程类型不能为空");
        }
        if (Objects.isNull(businessId)) {
            throw new ServiceErrorException("业务数据id不能为空");
        }
        List<Flow> flowList = flowMapper.getAuditHistoryFlowList(auditFlows, (long)businessId);
        if (CollectionUtils.isEmpty(flowList)) {
            return Lists.newArrayList();
        }
        List<FlowTask> flowTaskList = flowTaskMapper.getHistoryFlowTasks(
            flowList.stream().map(Flow::getId).collect(toList()));
        if (CollectionUtils.isEmpty(flowTaskList)) {
            return Lists.newArrayList();
        }
        List<FlowTaskUser> flowTaskUserList = flowTaskUserMapper.listTaskUsers(
            flowTaskList.stream().map(FlowTask::getId).collect(toList()));
        if (CollectionUtils.isEmpty(flowTaskUserList)) {
            return Lists.newArrayList();
        }

        List<FlowTaskUser> historyList = flowTaskUserList.stream()
            .filter(flowTaskUser -> Objects.nonNull(flowTaskUser.getAuditTime()))
            .sorted(Comparator.comparing(FlowTaskUser::getAuditTime)).collect(toList());
        Map<Integer, List<SysAttachment>> attachmentMap = Maps.newHashMap();

        Map<Long, FlowTask> taskMap = flowTaskList.stream().collect(toMap(FlowTask::getId, Function.identity()));
        historyList.forEach(history -> {
            FlowTask task = taskMap.get(history.getTaskId());
            history.setFlowId(task.getFlowId());
            history.setAttachmentList(attachmentMap.get(history.getId().intValue()));
            String auditor = history.getAuditorName();
            if (!history.getAuditUser().equals(history.getAuditor()) && isNotBlank(auditor)) {
                history.setNickName(history.getNickName() + "(" + auditor + "代签)");
            }
            history.setDesc(task.getNodeName() + (PASS.getValue().equals(history.getStatus()) ? "通过" : "驳回"));
        });
        Map<Long, List<FlowTaskUser>> histroyMap = historyList.stream().collect(groupingBy(FlowTaskUser::getFlowId));

        // 当前审批人
        List<FlowTaskUser> auditingList = flowTaskUserList.stream().filter(item -> AuditStatusEnum.isAuditing(taskMap.get(item.getTaskId()).getStatus()) && AuditStatusEnum.isAuditing(item.getStatus())).collect(toList());
        auditingList.forEach(item -> {
            FlowTask task = taskMap.get(item.getTaskId());
            item.setFlowId(task.getFlowId());
        });
        Map<Long, List<FlowTaskUser>> auditingMap = auditingList.stream().collect(groupingBy(FlowTaskUser::getFlowId));

        List<FlowTaskUser> result = Lists.newArrayList();
        final QuotationPriceVO priceInfo;
        final List<TqBargainPrice> tqBargainPriceList;
        if (auditFlows.contains(AuditFlowEnum.BARGAIN) ){
            priceInfo = tqQuotationService.getPriceInfo(Long.valueOf(businessId));
            tqBargainPriceList = priceInfo.getBargainPriceList();
        } else {
            priceInfo = null;
            tqBargainPriceList = Lists.newArrayList();
        }


        flowList.forEach(flow -> {
            // 1. 添加提交节点
            FlowTaskUser submit = new FlowTaskUser();
            submit.setDesc(flow.getPromoterName() + "提交[" + AuditFlowEnum.from(flow.getAuditFlow()).getName() + "]");
            submit.setAuditTime(flow.getCreateTime());
            if (Objects.nonNull(priceInfo) && flow.getAuditFlow().equals(AuditFlowEnum.BARGAIN.getValue()) && CollectionUtils.isNotEmpty(tqBargainPriceList)){
                TqBargainPrice current = tqBargainPriceList.get(tqBargainPriceList.size() -1);
                submit.setRemark("希望价格合计：" + priceInfo.getCurrency() + " " + formatter.format(current.getBargainPrice()) + ", 平均基价折率: " + current.getDiscountRate().setScale(4, BigDecimal.ROUND_HALF_UP).toString() + "%");
                tqBargainPriceList.remove(tqBargainPriceList.size() -1);
            }
            result.add(submit);

            // 2. 添加撤回节点
            if (ROLLBACK.getValue().equals(flow.getStatus())) {
                FlowTaskUser rollback = new FlowTaskUser();
                rollback.setDesc(flow.getPromoterName() + "撤回");
                rollback.setAuditTime(flow.getUpdateTime());
                result.add(rollback);
            }

            // 3. 添加审批节点
            List<FlowTaskUser> auditList = histroyMap.get(flow.getId());
            if (CollectionUtils.isNotEmpty(auditList)) {
                result.addAll(auditList);
            }

            // 4. 添加当前审批人
            List<FlowTaskUser> currentList = auditingMap.get(flow.getId());
            if (CollectionUtils.isNotEmpty(currentList)) {
                FlowTaskUser auditing = new FlowTaskUser();
                auditing.setStatus(INIT.getValue());
                auditing.setTaskId(currentList.get(0).getTaskId());
                auditing.setDesc(currentList.stream()
                        .map(FlowTaskUser::getNickName)
                        .collect(Collectors.joining(",")) + "正在审批中...");
                result.add(auditing);
            }
        });
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean finish(FlowAuditParam auditParam) {
        Flow flow = flowMapper.getBusinessFlow(auditParam.getAuditFlow(), auditParam.getBusinessId());
        if (Objects.isNull(flow)) {
            throw new ServiceErrorException("数据异常，审批流程数据不存在");
        }
        // 直接以当前状态结束流程
        List<FlowTask> tasks = flowTaskMapper.getFlowTasks(flow.getId());
        List<FlowTask> auditingList = tasks.stream().filter(flowTask -> flowTask.getStatus().equals(INIT.getValue())).collect(toList());
        if (CollectionUtils.isEmpty(auditingList)) {
            return true;
        }
        FlowTask currentTask = auditingList.get(0);

        if (Objects.nonNull(currentTask)) {
            currentTask.setStatus(DRAFT.getValue());
            currentTask.setUpdateTime(new Date());
            currentTask.setUpdateBy(SecurityUtils.getUserId().toString());
            flowTaskMapper.updateStatus(currentTask);

            FlowTaskUser param = new FlowTaskUser();
            List<FlowTaskUser> taskUsers = flowTaskUserMapper.listTaskUsers(Lists.newArrayList(currentTask.getId()));
            param.setIds(taskUsers.stream().map(FlowTaskUser::getId).collect(toList()));
            param.setStatus(DRAFT.getValue());
            param.setUpdateTime(new Date());
            param.setUpdateBy(SecurityUtils.getUserId().toString());
            flowTaskUserMapper.updateStatusBatch(param);
        }
        flow.setStatus(PASS.getValue());
        flow.setUpdateTime(new Date());
        flow.setUpdateBy(SecurityUtils.getUserId().toString());
        flowMapper.updateStatus(flow);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertCopy(FlowAuditParam auditParam, List<Integer> statusCodes) {
        Flow flow = flowMapper.getBusinessFlow(auditParam.getAuditFlow(), auditParam.getBusinessId());
        if (Objects.isNull(flow)) {
            throw new ServiceErrorException("数据异常，审批流程数据不存在");
        }
        List<FlowTask> tasks = flowTaskMapper.getFlowTasks(flow.getId());
        FlowTask finalTask = tasks.stream().filter(task -> StringUtils.isBlank(task.getNextTasks())).collect(toList()).get(0);

        // 新生成节点并设置前后关系
        List<FlowTask> targetTasks = tasks.stream().filter(task -> statusCodes.contains(task.getStatusCode()) && task.getNodeId()
                .split("-").length == 2).collect(toList());
        if (CollectionUtils.isEmpty(targetTasks)) {
            throw new ServiceErrorException("数据异常，节点数据不存在");
        }

        targetTasks.forEach(task -> {
            task.setOldId(task.getId());
            task.setNodeId(finalTask.getId() + "-" + task.getNodeId());
            task.setPrevTasks(StringUtils.isBlank(task.getPrevTasks())? finalTask.getNodeId(): finalTask.getId() + "-" + task.getPrevTasks());
            task.setNextTasks(StringUtils.isBlank(task.getNextTasks())? null: finalTask.getId() + "-" + task.getNextTasks());
            task.setStatus(DRAFT.getValue());
        });
        FlowTask flowTask = new FlowTask();
        flowTask.setId(finalTask.getId());
        flowTask.setNextTasks(targetTasks.get(0).getNodeId());
        flowTask.setUpdateTime(new Date());
        flowTask.setUpdateBy(SecurityUtils.getUserId().toString());
        flowTaskMapper.updateNextTasks(flowTask);
        // 激活第一个节点
        targetTasks.get(0).setStatus(INIT.getValue());
        targetTasks.get(targetTasks.size() - 1).setNextTasks(null);
        flowTaskMapper.saveBatch(targetTasks);


        Map<Long, Long> idMap = targetTasks.stream().collect(toMap(FlowTask::getOldId, FlowTask::getId));
        List<FlowTaskUser> taskUsers = flowTaskUserMapper.listTaskUsers(targetTasks.stream().map(FlowTask::getOldId).collect(toList()));
        taskUsers.forEach(taskUser -> {
            taskUser.setTaskId(idMap.get(taskUser.getTaskId()));
            taskUser.setRemark(null);
            taskUser.setAuditor(null);
            taskUser.setAuditTime(null);
            if (taskUser.getTaskId().equals(targetTasks.get(0).getId())){
                taskUser.setStatus(INIT.getValue());
            } else {
                taskUser.setStatus(DRAFT.getValue());
            }
        });

        flowTaskUserMapper.saveBatch(taskUsers);

        flow.setStatus(targetTasks.get(0).getStatusCode());
        flowMapper.updateStatus(flow);

        auditParam.setNextAuditUsers(
                taskUsers.stream().filter(flowTaskUser -> targetTasks.get(0).getId().equals(flowTaskUser.getTaskId()))
                        .map(FlowTaskUser::getAuditUser).map(Long::valueOf).collect(toList()));
       return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean alertAudit(Long taskId) {
        FlowTask task = flowTaskMapper.getById(taskId);
        if (!AuditStatusEnum.isAuditing(task.getStatus())) {
            throw new ServiceErrorException("数据异常，只有审批时才可发送待办");
        }
        Flow flow = flowMapper.getById(task.getFlowId());
        List<FlowTaskUser> flowTaskUsers = flowTaskUserMapper.listTaskUsers(Lists.newArrayList(task.getId()));
        List<Long> userIds = flowTaskUsers.stream().filter(item -> AuditStatusEnum.isAuditing(item.getStatus()))
                .map(FlowTaskUser::getAuditUser).map(Long::valueOf).collect(toList());

        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
        AuditFlowEnum flowEnum = AuditFlowEnum.from(flow.getAuditFlow());
        Object[] args = new String[2];
        args[0] = flow.getBusinessDesc();
        args[1] = task.getNodeName();
        String msg = flowEnum.getAlertMsg();
        String messageContent = messageSource.getMessage(msg, args, new Locale("zh","CN"));
        String messageContentUS = messageSource.getMessage(msg, args,new Locale("en","US"));
        String messageContentRU = messageSource.getMessage(msg, args,new Locale("ru","RU"));
        String messageContentES = messageSource.getMessage(msg, args,new Locale("es","ES"));

        Message message = new Message();
        message.setMessageType(MessageTypeEnum.ALERT_AUDIT.getValue());
        message.setMessageContent(messageContent);
        message.setMessageContentUS(messageContentUS);
        message.setMessageContentRU(messageContentRU);
        message.setMessageContentES(messageContentES);
        sseEmitterService.sendSaveMessage(userIds, message);

        List<String> emailList = sysUserService.listByIds(userIds).stream().map(SysUser::getEmail).collect(toList());
        BaseEmailEntity email = BaseEmailEntity.builder()
                .to(emailList.toArray(new String[emailList.size()]))
                .subject(MessageUtils.getMessage("site.name", 0))
                .text(MessageUtils.getMessage(msg, 0, args))
                .build();
        EmailUtil emailUtil = SpringUtil.getBean(EmailUtil.class);
        try {
            emailUtil.sendSimpleMail(email);
        } catch (Exception e) {
            log.error("邮件通知异常", e);
        }
        return true;
    }
}
