/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.security.trans.flow.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Preconditions;
import org.security.common.Constant;
import org.security.common.util.DataFormatUtil;
import org.security.trans.flow.dto.FlowStartDTO;
import org.security.trans.flow.entity.FlowData;
import org.security.trans.flow.entity.FlowHistory;
import org.security.trans.flow.entity.FlowNode;
import org.security.trans.flow.service.IFlowDataService;
import org.security.trans.flow.service.IFlowNodeService;
import org.security.trans.flow.vo.FlowHistoryVO;
import org.security.trans.flow.mapper.FlowHistoryMapper;
import org.security.trans.flow.service.IFlowHistoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.security.trans.flow.vo.FlowNodeDataVO;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 流程-流程历史记录表 服务实现类
 *
 * @author BladeX
 * @since 2022-03-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class FlowHistoryServiceImpl extends ServiceImpl<FlowHistoryMapper, FlowHistory> implements IFlowHistoryService {
	private final IFlowDataService flowDataService;
	private final IFlowNodeService flowNodeService;

	@Override
	public boolean startFlow(FlowStartDTO flowStartDTO){
		log.info("==============流程创建任务开始==============");
		//查看是否存在相应的流程模块
		FlowData flowData = flowDataService.getOne(new QueryWrapper<FlowData>().lambda().eq(Func.isNotBlank(flowStartDTO.getFlowBusinessCode()), FlowData::getFlowBusinessCode, flowStartDTO.getFlowBusinessCode())
			.eq(FlowData::getFlowStatus, "0"));
		Preconditions.checkArgument(Func.isEmpty(flowData), "该业务模块没有相关的流程配置，请联系管理员处理，业务模块名称：【】", flowStartDTO.getFlowBusinessCode());
		//获取相关流程节点信息
		List<FlowNode> flowNodeList = flowNodeService.list(new QueryWrapper<FlowNode>().eq("FLOW_ID", flowData.getId()));
		Preconditions.checkArgument(Func.isEmpty(flowNodeList), "改业务模块没有没有相关的流程节点配置,请联系管理员处,理业务模块名称：【】", flowStartDTO.getFlowBusinessCode());
		//筛选出开始节点
		List<FlowNode> collect = flowNodeList.stream().filter(item -> "开始".equals(item.getFlowNodeName())).collect(Collectors.toList());
		Preconditions.checkArgument(Func.isEmpty(collect), "该业务模块流程节点配置错误，请联系管理员处理，理业务模块名称：【】", flowStartDTO.getFlowBusinessCode());
		//筛选出开始节点的下一节点
		List<FlowNode> nodeList = flowNodeList.stream().filter(item -> collect.get(0).getNextNodeId().equals(item.getId())).collect(Collectors.toList());
		Preconditions.checkArgument(Func.isEmpty(nodeList), "该业务模块流程节点配置错误，请联系管理员处理，理业务模块名称：【】", flowStartDTO.getFlowBusinessCode());
		//保存节点历史表记录
		ArrayList<FlowHistory> list = new ArrayList<>();
		//开始节点
		String currentNodeFlag = "0";
		setFlowHistory(flowStartDTO, collect, list, currentNodeFlag);
		//开始节点的下一节点
		String currentNodeFlag2 = "1";
		setFlowHistory(flowStartDTO, nodeList, list, currentNodeFlag2);
		boolean b = super.saveBatch(list);
		Preconditions.checkArgument(!b,"创建流程失败，业务数据为：",flowStartDTO);
		log.info("==============流程创建任务结束==============");
		return b;
	}

	@Override
	public R approvalPassData(FlowStartDTO flowStartDTO) {
		log.info("==============审批通过流程开始==============");
		//获取流程历史表中是否存在目标业务模块、目标业务数据id、目标角色关联的，并且处于单前节点相关的节点数据
		FlowHistory flowHistory = super.getOne(new QueryWrapper<FlowHistory>().lambda()
			.eq(Func.isNotBlank(flowStartDTO.getBusinessLinkId()), FlowHistory::getBusinessLinkId, flowStartDTO.getBusinessLinkId())
			.eq(FlowHistory::getFlowRoleId, AuthUtil.getUser().getRoleId())
			.eq(FlowHistory::getCurrentNodeFlag, "1"));
		if (Func.isEmpty(flowHistory)) {
			log.info("该业务数据已经被其他用户审批完成，无法再次审批");
			return R.fail("该业务数据已经被其他用户审批完成，无法再次审批");
		}
		//获取下一节点信息
		FlowNode flowNode = flowNodeService.getById(flowHistory.getNextNodeId());
		if (Func.isEmpty(flowNode)) {
			log.error("该业务模块审批节点配置存在问题，请联系管理员处理，业务数据为【】" + flowStartDTO);
			return R.fail("该业务模块审批节点配置存在问题，请联系管理员处理，业务数据为【】" + flowStartDTO);
		}
		//保存新节点信息,更新之前节点数据的当前节点标识符为0
		boolean b = saveNextNodeData(flowStartDTO.getBusinessLinkId(), flowNode);
		boolean b1 = updatePreNode(flowHistory, flowStartDTO);
		log.info("==============审批通过流程结束==============");
		if (b && b1) {
			log.info("审批通过");
			return R.success("审批通过");
		}else {
			log.error("审批通过失败，业务数据为【】" + flowStartDTO);
		    return R.fail("审批通过失败，请联系管理员");
		}
	}

	@Override
	public boolean passData(FlowStartDTO flowStartDTO) {
		log.info("==============任务提报流程开始==============");
		//获取流程历史表中是否存在目标业务模块、目标业务数据id、目标角色关联的，并且处于单前节点相关的提报节点数据
		FlowHistory flowHistory = super.getOne(new QueryWrapper<FlowHistory>().lambda()
			.eq(Func.isNotBlank(flowStartDTO.getBusinessLinkId()), FlowHistory::getBusinessLinkId, flowStartDTO.getBusinessLinkId())
			.eq(FlowHistory::getFlowRoleId, AuthUtil.getUser().getRoleId())
			.eq(FlowHistory::getFlowNodeName, "提报")
			.eq(FlowHistory::getCurrentNodeFlag, "1"));
		Preconditions.checkArgument(Func.isEmpty(flowHistory), "不存在相关需要提报的业务数据，请联系管理员，业务数据为【】", flowStartDTO);
		//获取下一节点信息
		FlowNode flowNode = new FlowNode();
		//当前节点是智能驳回后的节点
		if (Constant.USUAL_STATE_TURE.equals(flowHistory.getRefuseFlag())) {
			//获取智能驳回后节点在流程历史表中的上一个节点
			List<FlowHistory> list = this.list(new QueryWrapper<FlowHistory>().lambda()
				.eq(Func.isNotBlank(flowStartDTO.getBusinessLinkId()), FlowHistory::getBusinessLinkId, flowStartDTO.getBusinessLinkId()).orderByDesc(FlowHistory::getUpdatedTime));
			Preconditions.checkArgument(Func.isEmpty(list), "智能驳回后数据");
			//因为list按更新时间排序后第二条数据就是智能驳回节点数据
			Func.copy(list.get(1), flowNode);
		} else {
			flowNode = flowNodeService.getById(flowHistory.getNextNodeId());
		}
		Preconditions.checkArgument(Func.isEmpty(flowNode), "该业务模块流程节点配置错误，请联系管理员，业务相关数据【】", flowStartDTO);
		//保存新节点信息,更新之前节点数据的当前节点标识符为0
		boolean b = saveNextNodeData(flowStartDTO.getBusinessLinkId(), flowNode);
		boolean b1 = updatePreNode(flowHistory, flowStartDTO);
		log.info("==============任务提报流程结束==============");
		return b && b1;
	}

	/**
	 * 更新之前节点数据的当前节点标识符为0
	 */
	private boolean updatePreNode(FlowHistory flowHistory, FlowStartDTO flowStartDTO) {
		flowHistory.setCurrentNodeFlag("0");
		flowHistory.setApproveStatus(flowStartDTO.getApproveStatus());
		flowHistory.setRemarks(flowStartDTO.getRemarks());
		return super.updateById(flowHistory);
	}

	/**
	 * 保存新节点信息
	 */
	private boolean saveNextNodeData(String dataId, FlowNode flowNode) {
		FlowHistory history = Func.copy(flowNode, FlowHistory.class);
		history.setId(null);
		history.setFlowNodeId(flowNode.getId());
		history.setBusinessLinkId(dataId);
		history.setCurrentNodeFlag("1");
		history.setRefuseFlag(Constant.USUAL_STATE_FALSE);
		return super.save(history);
	}

	@Override
	public FlowNodeDataVO getRoleId(List<String> flowBusinessCodes, String roleId) {
		log.info("==============获取流程是否处于单前节点的业务关联数据id集合开始==============");
		FlowNodeDataVO nodeDataVO = new FlowNodeDataVO();
		//模块流程标识符不为空
		if (Func.isNotEmpty(flowBusinessCodes)) {
			//获取流程模块id
			ArrayList<String> arrayList = new ArrayList<>();
			List<FlowData> flowData = flowDataService.getFlowData(flowBusinessCodes);
			if (Func.isNotEmpty(flowData)) {
				flowData.forEach(item -> {
					arrayList.add(item.getId());
				});
			}
			//获取流程历史表数据
			List<FlowHistory> flowHistoryList = baseMapper.getFlowHistoryData(roleId, arrayList);
			if (Func.isNotEmpty(flowHistoryList)) {
				setDataId(nodeDataVO, flowHistoryList);
			}
		} else {
			//模块流程标识符为空
			List<FlowHistory> historyList = super.list(new QueryWrapper<FlowHistory>().eq("FLOW_ROLE_ID", roleId));
			setDataId(nodeDataVO, historyList);
		}
		log.info("==============获取流程是否处于单前节点的业务关联数据id集合结束==============");
		return nodeDataVO;
	}

	/**
	 * 设置返回数据id
	 */
	private void setDataId(FlowNodeDataVO nodeDataVO, List<FlowHistory> flowHistoryList) {
		//当前节点并且是提报节点数据id
		List<FlowHistory> currDataIdList = flowHistoryList.stream().filter(item -> "1".equals(item.getCurrentNodeFlag()) && "提报".equals(item.getFlowNodeName())).collect(Collectors.toList());
		//当前节点并且不是提报节点数据id
		List<FlowHistory> currList = flowHistoryList.stream().filter(item -> "1".equals(item.getCurrentNodeFlag()) && !"提报".equals(item.getFlowNodeName())).collect(Collectors.toList());
		//筛选出不处于当前节点的数据id
		List<FlowHistory> dataIdList = flowHistoryList.stream().filter(item -> "0".equals(item.getCurrentNodeFlag())).collect(Collectors.toList());
		if (Func.isNotEmpty(currDataIdList)) {
			ArrayList<String> currIdList = new ArrayList<>();
			currDataIdList.forEach(item -> {
				currIdList.add(item.getBusinessLinkId());
			});
			//list去重
			ArrayList<String> stringArrayList = setDataIdList(currIdList);
			nodeDataVO.setCurrDataList(stringArrayList);
		}
		if (Func.isNotEmpty(currList)) {
			ArrayList<String> curr = new ArrayList<>();
			currList.forEach(item -> {
				curr.add(item.getBusinessLinkId());
			});
			ArrayList<String> stringArrayList = setDataIdList(curr);
			nodeDataVO.setCurrList(stringArrayList);
		}
		if (Func.isNotEmpty(dataIdList)) {
			ArrayList<String> idList = new ArrayList<>();
			dataIdList.forEach(item -> {
				idList.add(item.getBusinessLinkId());
			});
			//list去重
			ArrayList<String> stringArrayList = setDataIdList(idList);
			nodeDataVO.setDataIdList(stringArrayList);
		}
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public R rejectData(FlowStartDTO flowStartDTO){
		log.info("==============业务流程驳回开始==============");
		FlowHistory flowHistory = super.getOne(new QueryWrapper<FlowHistory>().lambda()
			.eq(Func.isNotBlank(flowStartDTO.getBusinessLinkId()), FlowHistory::getBusinessLinkId, flowStartDTO.getBusinessLinkId())
			.eq(FlowHistory::getFlowRoleId, AuthUtil.getUser().getRoleId())
			.eq(FlowHistory::getCurrentNodeFlag, "1"));
		if (Func.isEmpty(flowHistory)) {
			log.info("该业务已经被驳回，请勿重复提交驳回操作,驳回申请数据为【】" + flowStartDTO);
			return R.fail(409, "该业务已经被驳回，请勿重复提交驳回操作");
		}
		//获取提报节点
		FlowNode flowNode = flowNodeService.getOne(new QueryWrapper<FlowNode>().lambda()
			.eq(Func.isNotBlank(flowHistory.getFlowId()), FlowNode::getFlowId, flowHistory.getFlowId())
			.eq(FlowNode::getFlowNodeName, "提报"));
		if (Func.isEmpty(flowNode)) {
			log.info("任务驳回失败，该业务流程节点配置存在问题，请联系管理员,驳回申请数据为【】" + flowStartDTO);
			return R.fail(410, "任务驳回失败，该业务流程节点配置存在问题，请联系管理员");
		}
		//将流程历史表里业务数据id对应的最新当前节点数据更新为提报节点
		boolean b = saveNodeData(flowStartDTO, flowNode);
		//更新之前节点数据的当前节点标识符为非当前节点
		boolean b1 = updatePreNode(flowHistory, flowStartDTO);
		log.info("==============业务流程驳回结束==============");
		if (b && b1){
			log.info("任务驳回成功");
			return R.success("任务驳回成功");
		}else {
			log.info("任务驳回失败，请联系管理员,驳回申请数据为【】" + flowStartDTO);
			return R.success("任务驳回失败");
		}
	}

	@Override
	public List<FlowHistoryVO> getFlowHistory(FlowStartDTO flowStartDTO) {
		log.info("==============获取业务流程历史开始==============");
		List<FlowHistory> flowHistoryList = this.list(new QueryWrapper<FlowHistory>().lambda()
			.eq(Func.isNotBlank(flowStartDTO.getBusinessLinkId()), FlowHistory::getBusinessLinkId, flowStartDTO.getBusinessLinkId()));
		List<FlowHistoryVO> flowHistoryVOS = Func.copy(flowHistoryList, FlowHistoryVO.class);
		log.info("==============获取业务流程历史结束==============");
		return flowHistoryVOS;
	}

	/**
	 *将流程历史表里业务数据id对应的最新当前节点数据更新为提报节点
	 */
	private boolean saveNodeData(FlowStartDTO flowStartDTO, FlowNode flowNode) {
		FlowHistory history = Func.copy(flowNode, FlowHistory.class);
		history.setId("");
		history.setCurrentNodeFlag("1");
		history.setFlowNodeId(flowNode.getId());
		history.setRemarks(flowStartDTO.getRemarks());
		history.setApproveStatus(flowStartDTO.getApproveStatus());
		history.setBusinessLinkId(flowStartDTO.getBusinessLinkId());
		history.setRefuseFlag(Constant.USUAL_STATE_FALSE.equals(flowStartDTO.getRejectFlag()) ? Constant.USUAL_STATE_FALSE : Constant.USUAL_STATE_TURE);
		return this.save(history);
	}

	/**
	 * list去重
	 */
	private ArrayList<String> setDataIdList(ArrayList<String> currIdList) {
		HashSet<String> set = new HashSet<>();
		ArrayList<String> stringArrayList = new ArrayList<>();
		currIdList.forEach(item -> {
			if (set.add(item)) {
				stringArrayList.add(item);
			}
		});
		return stringArrayList;
	}

	/**
	 * 设置流程历史数据
	 */
	private void setFlowHistory(FlowStartDTO flowStartDTO, List<FlowNode> collect, ArrayList<FlowHistory> list, String currentNodeFlag){
		FlowHistory flowHistory = Func.copy(collect.get(0), FlowHistory.class);
		flowHistory.setId(null);
		flowHistory.setRefuseFlag(Constant.USUAL_STATE_FALSE);
		flowHistory.setFlowNodeId(collect.get(0).getId());
		flowHistory.setBusinessLinkId(flowStartDTO.getBusinessLinkId());
		flowHistory.setCurrentNodeFlag(currentNodeFlag);
		list.add(flowHistory);
	}

}
