package com.njcn.bpm.utils;

import com.njcn.bpm.listener.BpmProcessInstanceStatusEvent;
import com.njcn.bpm.pojo.dto.BpmInstanceInfo;
import com.njcn.bpm.pojo.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import com.njcn.bpm.pojo.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import com.njcn.bpm.pojo.dto.PageResult;
import com.njcn.bpm.pojo.po.BpmCategory;
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO;
import com.njcn.supervision.api.BusinessCommonFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.vo.UserVO;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 流程实例 Convert
 *
 * @author hongawen
 */
@Mapper
public interface BpmProcessInstanceConvert {

    BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);

    default PageResult<BpmProcessInstanceVO> buildProcessInstancePage(PageResult<HistoricProcessInstance> pageResult,
                                                                      Map<String, ProcessDefinition> processDefinitionMap,
                                                                      Map<String, BpmCategory> categoryMap,
                                                                      Map<String, List<Task>> taskMap,
                                                                      Map<String, UserVO> userMap,
                                                                      Map<String, Dept> deptMap,
                                                                      BusinessCommonFeignClient businessCommonFeignClient) {
        PageResult<BpmProcessInstanceVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceVO.class);
        for (int i = 0; i < pageResult.getList().size(); i++) {
            BpmProcessInstanceVO respVO = vpPageResult.getList().get(i);
            respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));
            MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
                    processDefinition -> respVO.setCategory(processDefinition.getCategory()));
            MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
            respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceVO.Task.class));
            // user
            if (userMap != null) {
                UserVO startUser = userMap.get(pageResult.getList().get(i).getStartUserId());
                if (startUser != null) {
                    respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class));
                    MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
                }
            }
            String processDefinitionId = respVO.getProcessDefinitionId();
            String key = processDefinitionId.substring(0,processDefinitionId.lastIndexOf(":"));
            key = key.substring(0,key.lastIndexOf(":"));
            BpmInstanceInfo instanceInfo = businessCommonFeignClient.getInstanceInfo(key, respVO.getBusinessKey()).getData();
            if(Objects.nonNull(instanceInfo)){
                respVO.setHistoryInstanceId(instanceInfo.getHistoryInstanceId());
                respVO.setInstanceSign(instanceInfo.getInstanceSign());
            }
        }
        return vpPageResult;
    }

    default List<BpmProcessInstanceVO> buildProcessInstancePage(List<HistoricProcessInstance> instances,
                                                                      Map<String, ProcessDefinition> processDefinitionMap,
                                                                      Map<String, BpmCategory> categoryMap,
                                                                      Map<String, List<Task>> taskMap,
                                                                      Map<String, UserVO> userMap,
                                                                      Map<String, Dept> deptMap,
                                                                      BusinessCommonFeignClient businessCommonFeignClient) {
        List<BpmProcessInstanceVO> vpInstancesResult = BeanUtils.toBean(instances, BpmProcessInstanceVO.class);
        for (int i = 0; i <instances.size(); i++) {
            BpmProcessInstanceVO respVO = vpInstancesResult.get(i);
            respVO.setStatus(FlowableUtils.getProcessInstanceStatus(instances.get(i)));
            MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
                    processDefinition -> respVO.setCategory(processDefinition.getCategory()));
            MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
            respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceVO.Task.class));
            // user
            if (userMap != null) {
                UserVO startUser = userMap.get(instances.get(i).getStartUserId());
                if (startUser != null) {
                    respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class));
                    MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
                }
            }
            String processDefinitionId = respVO.getProcessDefinitionId();
            String key = processDefinitionId.substring(0,processDefinitionId.lastIndexOf(":"));
            key = key.substring(0,key.lastIndexOf(":"));
            BpmInstanceInfo instanceInfo = businessCommonFeignClient.getInstanceInfo(key, respVO.getBusinessKey()).getData();
            if(Objects.nonNull(instanceInfo)){
                respVO.setHistoryInstanceId(instanceInfo.getHistoryInstanceId());
                respVO.setInstanceSign(instanceInfo.getInstanceSign());
            }
        }
        return vpInstancesResult;
    }

    default BpmProcessInstanceVO buildProcessInstance(HistoricProcessInstance processInstance,
                                                      ProcessDefinition processDefinition,
                                                      BpmProcessDefinitionInfo processDefinitionExt,
                                                      String bpmnXml,
                                                      UserVO startUser,
                                                      Dept dept) {
        BpmProcessInstanceVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceVO.class);
        respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
        respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
        // definition
        respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class));
        copyTo(processDefinitionExt, respVO.getProcessDefinition());
        respVO.getProcessDefinition().setBpmnXml(bpmnXml);
        // user
        if (startUser != null) {
            respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class));
            if (dept != null) {
                respVO.getStartUser().setDeptName(dept.getName());
            }
        }
        return respVO;
    }

    @Mapping(source = "from.id", target = "to.id", ignore = true)
    void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to);

    default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, HistoricProcessInstance instance, Integer status) {
        BpmProcessInstanceStatusEvent bpmProcessInstanceStatusEvent = new BpmProcessInstanceStatusEvent(source);
        bpmProcessInstanceStatusEvent.setId(instance.getId());
        bpmProcessInstanceStatusEvent.setStatus(status);
        bpmProcessInstanceStatusEvent.setProcessDefinitionKey(instance.getProcessDefinitionKey());
        bpmProcessInstanceStatusEvent.setBusinessKey(instance.getBusinessKey());
        return bpmProcessInstanceStatusEvent;
    }

    default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance,String reason, Integer status) {
        BpmProcessInstanceStatusEvent bpmProcessInstanceStatusEvent = new BpmProcessInstanceStatusEvent(source);
        bpmProcessInstanceStatusEvent.setId(instance.getId());
        bpmProcessInstanceStatusEvent.setStatus(status);
        bpmProcessInstanceStatusEvent.setProcessDefinitionKey(instance.getProcessDefinitionKey());
        bpmProcessInstanceStatusEvent.setBusinessKey(instance.getBusinessKey());
        bpmProcessInstanceStatusEvent.setReason(reason);
        return bpmProcessInstanceStatusEvent;
    }

    default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) {
        BpmMessageSendWhenProcessInstanceApproveReqDTO bpmMessageSendWhenProcessInstanceApproveReqDTO = new BpmMessageSendWhenProcessInstanceApproveReqDTO();
        bpmMessageSendWhenProcessInstanceApproveReqDTO.setStartUserId(instance.getStartUserId());
        bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceId(instance.getId());
        bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceName(instance.getName());

        return bpmMessageSendWhenProcessInstanceApproveReqDTO;
    }

    default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) {
        BpmMessageSendWhenProcessInstanceRejectReqDTO bpmMessageSendWhenProcessInstanceRejectReqDTO = new BpmMessageSendWhenProcessInstanceRejectReqDTO();
        bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceName(instance.getName());
        bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceId(instance.getId());
        bpmMessageSendWhenProcessInstanceRejectReqDTO.setReason(reason);
        bpmMessageSendWhenProcessInstanceRejectReqDTO.setStartUserId(instance.getStartUserId());
        return bpmMessageSendWhenProcessInstanceRejectReqDTO;
    }


}
