package com.fjhx.service.example.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.BaseEntity;
import com.fjhx.constants.FlowConstant;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.example.ExampleDetails;
import com.fjhx.entity.example.ExampleInfo;
import com.fjhx.entity.process.ProcessNode;
import com.fjhx.entity.process.ProcessNodeButton;
import com.fjhx.entity.process.ProcessTenant;
import com.fjhx.enums.ButtonNameEnum;
import com.fjhx.enums.HandleResultEnum;
import com.fjhx.enums.ProcessNodeHandleObjectTypeEnum;
import com.fjhx.enums.ProcessNodeTypeEnum;
import com.fjhx.mapper.example.ExampleInfoMapper;
import com.fjhx.service.example.ExampleDetailsService;
import com.fjhx.service.example.ExampleInfoService;
import com.fjhx.service.process.ProcessNodeButtonService;
import com.fjhx.service.process.ProcessNodeService;
import com.fjhx.service.process.ProcessTenantService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.UserClientUtil;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 流程实例 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-08-18
 */
@Service
public class ExampleInfoServiceImpl extends ServiceImpl<ExampleInfoMapper, ExampleInfo> implements ExampleInfoService {

    @Lazy
    @Autowired
    private ProcessNodeService processNodeService;

    @Autowired
    private ExampleDetailsService exampleDetailsService;

    @Lazy
    @Autowired
    private ProcessNodeButtonService processNodeButtonService;

    @Lazy
    @Autowired
    private ProcessTenantService processTenantService;

    @Override
    public Page<Map<String, Object>> getWaitingProcessingPage(Map<String, String> condition) {

        BladeUser user = AuthUtil.getUser();
        Assert.notEmpty(user, "没有找到用户信息");

        Long userId = user.getUserId();
        String[] roleIds = user.getRoleId().split(",");
        String[] deptIds = user.getDeptId().split(",");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("ei.complete", StatusConstant.NO)
                .and(q -> q
                        .eq("ei.handle_object_type", ProcessNodeHandleObjectTypeEnum.ALL.getType())
                        .or(e -> e
                                .eq("ei.handle_object_type", ProcessNodeHandleObjectTypeEnum.USER.getType())
                                .like("ei.handle_object_id_set", userId))
                        .or(e -> e
                                .eq("ei.handle_object_type", ProcessNodeHandleObjectTypeEnum.ROLE.getType())
                                .and(w -> {
                                    for (String roleId : roleIds) {
                                        w.or().like("ei.handle_object_id_set", roleId);
                                    }
                                }))
                        .or(e -> e
                                .eq("ei.handle_object_type", ProcessNodeHandleObjectTypeEnum.DEPT.getType())
                                .and(w -> {
                                    for (String deptId : deptIds) {
                                        w.or().like("ei.handle_object_id_set", deptId);
                                    }
                                }))
                );

        Page<Map<String, Object>> waitingProcessingPage = baseMapper.getWaitingProcessingPage(createPage(condition), wrapper);

        // 赋值创建人名称
        List<Map<String, Object>> records = waitingProcessingPage.getRecords();

        if (records.size() > 0) {
            // 赋值用户名称
            UserClientUtil.setUserName(records, "createUser", "userName");
        }

        return waitingProcessingPage;
    }

    @Override
    public List<Map<String, Object>> record(Long flowLinkNo) {
        Assert.notEmpty(flowLinkNo, "业务关联编码不能为空");

        // 流程实例
        ExampleInfo exampleInfo = getOne(ExampleInfo::getFlowLinkNo, flowLinkNo);

        // 查询已处理节点
        List<Map<String, Object>> result = exampleDetailsService.listMaps(
                Wrappers.<ExampleDetails>query()
                        .select("example_info_id exampleInfoId",
                                "id exampleDetailsId",
                                "handle_user_id userId", // 处理人id
                                "remarks", // 审批意见
                                "name_type type", // 处理类型
                                "process_node_id nodeId", // 节点id
                                "create_time createTime", // 创建时间
                                "create_user createUser" // 创建人
                        )
                        .lambda()
                        .eq(ExampleDetails::getExampleInfoId, exampleInfo.getId())
                        .orderByAsc(BaseEntity::getId));


        // 查询用户名称与岗位
        List<Long> userId = result.stream().map(item -> (Long) item.get("userId")).collect(Collectors.toList());
        Map<Long, Map<String, Object>> userNameAndPost = UserClientUtil.getNameByUserId(userId);

        // 查询所有节点
        List<ProcessNode> processNodeList = processNodeService.list(ProcessNode::getProcessTenantId, exampleInfo.getProcessTenantId());
        // 根据节点id封装为map
        Map<Long, ProcessNode> processNodeMap = processNodeList.stream().collect(Collectors.toMap(ProcessNode::getId, item -> item));
        // 根据父节点id封装为map
        Map<Long, List<ProcessNode>> parentProcessNodeMap = processNodeList.stream().collect(Collectors.groupingBy(ProcessNode::getParentId));

        for (Map<String, Object> map : result) {
            ProcessNode processNode = processNodeMap.get((Long) map.get("nodeId"));
            // 处理类型名称
            map.put("typeName", ButtonNameEnum.get((Integer) map.get("type")).getName());
            // 赋值节点名称
            map.put("nodeName", processNode == null ? FlowConstant.END_NAME : processNode.getName());
            // 节点类型
            map.put("nodeType", processNode == null ? ProcessNodeTypeEnum.END.getType() : processNode.getType());

            // 赋值用户名称与岗位
            Map<String, Object> userNameAndPostMap = userNameAndPost.get((Long) map.get("userId"));
            if (userNameAndPostMap != null) {
                map.putAll(userNameAndPostMap);
            }
        }

        // 下一个节点进行中
        ProcessNode nextNode = processNodeMap.get(exampleInfo.getProcessNodeId());

        // 没有未完成的节点或者下一节点为结束
        if (nextNode == null || ProcessNodeTypeEnum.END.getType().equals(nextNode.getType())) {
            return result;
        }

        Map<String, Object> lastNode = result.get(result.size() - 1);

        // 如果最后一个节点处理人是自己，可以撤回
        if (result.size() > 1 && Convert.toLong(lastNode.get("userId")).equals(AuthUtil.getUserId())) {
            lastNode.put("withdrawProcessNodeId", result.get(result.size() - 2).get("nodeId").toString());
            lastNode.put("withdrawExampleInfoId", lastNode.get("exampleInfoId").toString());
            lastNode.put("withdrawExampleDetailsId", lastNode.get("exampleDetailsId").toString());
        }

        // 如果下一个节点是开始节点,发起人是自己，可以撤销
        if (FlowConstant.START_CODE.equals(nextNode.getCode()) &&
                AuthUtil.getUserId().equals(Convert.toLong(result.get(0).get("createUser")))) {
            lastNode.put("revokeExampleInfoId", lastNode.get("exampleInfoId").toString());
            lastNode.put("revokeExampleDetailsId", lastNode.get("exampleDetailsId").toString());
        }

        next(result, nextNode, parentProcessNodeMap);

        return result;
    }

    @Override
    public List<Map<String, Object>> showFlow(String code) {
        String tenantId = AuthUtil.getTenantId();
        ProcessTenant processTenant = processTenantService.getProcessTenant(code, tenantId);
        Assert.notEmpty(processTenant, "无法通过流程编码找到流程");

        List<ProcessNode> processNodeList = processNodeService.list(ProcessNode::getProcessTenantId, processTenant.getId());

        // 根据父节点id封装为map
        Map<Long, List<ProcessNode>> parentProcessNodeMap = processNodeList.stream().collect(Collectors.groupingBy(ProcessNode::getParentId));

        ProcessNode nextNode = new ProcessNode();
        for (ProcessNode processNode : processNodeList) {
            if (FlowConstant.START_CODE.equals(processNode.getCode())) {
                nextNode = processNode;
                break;
            }
        }

        List<Map<String, Object>> result = new ArrayList<>();
        next(result, nextNode, parentProcessNodeMap);
        return result;
    }

    @Override
    public Page<Map<String, Object>> getDone(Map<String, String> condition) {
        String title = condition.get("title");
        String processInfoId = condition.get("processInfoId");
        String status = condition.get("status");

        QueryWrapper<Object> wrapper = Wrappers.query();
        wrapper.eq("ei.del_flag", 0)
                .like(ObjectUtil.isNotEmpty(title), "ei.title", title)
                .eq(ObjectUtil.isNotEmpty(processInfoId), "pi.id", processInfoId)
                .eq(ObjectUtil.isNotEmpty(status), "ei.handle_result", status)
                .orderByDesc("ei.id");

        Long userId = AuthUtil.getUserId();
        Page<Map<String, Object>> page = baseMapper.getDone(createPage(condition), userId, wrapper);

        List<Map<String, Object>> records = page.getRecords();

        if (records.size() == 0) {
            return page;
        }

        // 创建人id集合
        List<Long> userIdList = new ArrayList<>();
        ArrayList<Long> processNodeIdList = new ArrayList<>();

        for (Map<String, Object> record : records) {

            Long createUserId = Convert.toLong(record.get("createUser"));
            Integer complete = Convert.toInt(record.get("complete"));
            String processNodeCode = Convert.toStr(record.get("processNodeCode"));
            Integer handleResult = Convert.toInt(record.get("handleResult"));
            Long processNodeId = Convert.toLong(record.get("processNodeId"));

            // 添加userId
            userIdList.add(createUserId);

            // 赋值当前节点
            if (StatusConstant.YES.equals(complete)) {
                record.put("currentNode", "结束");
            } else if (FlowConstant.START_CODE.equals(processNodeCode)) {
                record.put("currentNode", "开始");
            } else {
                processNodeIdList.add(processNodeId);
            }

            // 赋值流程状态
            record.put("flowStatus", HandleResultEnum.getName(handleResult));
        }

        Map<Long, String> userNameMap = UserClientUtil.getUserNameMap(userIdList);

        Map<Long, String> processNodeNameMap = new HashMap<>();
        if (processNodeIdList.size() > 0) {
            processNodeNameMap = processNodeService.list(Wrappers.<ProcessNode>lambdaQuery()
                            .select(ProcessNode::getId, ProcessNode::getName)
                            .in(ProcessNode::getId, processNodeIdList))
                    .stream()
                    .collect(Collectors.toMap(
                            ProcessNode::getId,
                            ProcessNode::getName,
                            (v1, v2) -> v1
                    ));
        }

        for (Map<String, Object> record : records) {
            // 赋值用户id
            record.put("createUserName", userNameMap.get(Convert.toLong(record.get("createUser"))));
            // 赋值当前节点
            record.putIfAbsent("currentNode", processNodeNameMap.get(Convert.toLong(record.get("processNodeId"))));
        }

        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void withdraw(Long withdrawExampleInfoId, Long withdrawExampleDetailsId) {
        List<ExampleDetails> list = exampleDetailsService.list(
                Wrappers.<ExampleDetails>lambdaQuery()
                        .eq(ExampleDetails::getExampleInfoId, withdrawExampleInfoId)
                        .ge(BaseEntity::getId, withdrawExampleDetailsId));
        Assert.eqOne(list.size(), "下一审批节点已被处理，无法撤回");

        ExampleDetails exampleDetails = list.get(0);
        ProcessNode processNode = processNodeService.getById(exampleDetails.getProcessNodeId());

        ExampleInfo exampleInfo = new ExampleInfo();
        exampleInfo.setId(withdrawExampleInfoId);
        exampleInfo.setProcessNodeId(processNode.getId());
        exampleInfo.setProcessNodeCode(processNode.getCode());
        updateById(exampleInfo);

        exampleDetailsService.removeById(exampleDetails.getId());
    }

    @Override
    public void revoke(Long withdrawExampleInfoId) {
        ExampleInfo exampleInfo = getById(withdrawExampleInfoId);
        Assert.eqTrue(FlowConstant.START_CODE.equals(exampleInfo.getProcessNodeCode()),
                "下一节点已被处理，不可撤销");

        exampleInfo.setHandleResult(HandleResultEnum.REVOKE.getType());
        exampleInfo.setComplete(StatusConstant.YES);
        updateById(exampleInfo);
    }


    /**
     * 寻找未开始节点
     */
    private void next(List<Map<String, Object>> result, ProcessNode nextNode, Map<Long, List<ProcessNode>> parentProcessNodeMap) {

        HashMap<String, Object> item = new HashMap<>();
        item.put("type", ButtonNameEnum.HAVE_IN_HAND.getType());
        item.put("typeName", ButtonNameEnum.HAVE_IN_HAND.getName());
        item.put("nodeId", nextNode.getId());
        item.put("nodeName", nextNode.getName());
        item.put("nodeType", nextNode.getType());

        // 节点处理按钮
        List<ProcessNodeButton> list = processNodeButtonService.list(ProcessNodeButton::getProcessNodeId, nextNode.getId());
        List<HashMap<Object, Object>> handleButtonList = list.stream().map(ProcessNodeButtonItem -> {
            HashMap<Object, Object> itemResult = new HashMap<>();
            itemResult.put("id", ProcessNodeButtonItem.getId());
            itemResult.put("type", ProcessNodeButtonItem.getNameType());
            itemResult.put("typeName", ButtonNameEnum.getName(ProcessNodeButtonItem.getNameType()));
            return itemResult;
        }).collect(Collectors.toList());
        item.put("buttonList", handleButtonList);
        result.add(item);

        // 寻找之后未处理的节点（未开始）
        List<ProcessNode> nextProcessNodes = parentProcessNodeMap.get(nextNode.getId());
        if (nextProcessNodes != null && nextProcessNodes.size() == 1) {
            nextNode = nextProcessNodes.get(0);

            while (nextNode != null) {
                HashMap<String, Object> map = new HashMap<>();
                map.put("type", ButtonNameEnum.NOT_STARTED.getType());
                map.put("typeName", ButtonNameEnum.NOT_STARTED.getName());
                map.put("nodeId", nextNode.getId());
                map.put("nodeName", nextNode.getName());
                map.put("nodeType", nextNode.getType());
                result.add(map);

                List<ProcessNode> processNodes = parentProcessNodeMap.get(nextNode.getId());
                if (processNodes != null && processNodes.size() == 1) {
                    nextNode = processNodes.get(0);
                } else {
                    nextNode = null;
                }
            }
        }
    }

}
