package com.huigou.bpm.engine.delegate;

import com.alibaba.fastjson.JSONObject;
import com.huigou.bpm.management.application.ProcessVariableApplication;
import com.huigou.bpm.management.application.ServiceParamApplication;
import com.huigou.bpm.management.domain.model.*;
import com.huigou.bpm.management.repository.ProcessDeploymentRepository;
import com.huigou.bpm.management.repository.ServiceMappingRepository;
import com.huigou.bpm.management.repository.ServiceParamMappingRepository;
import com.huigou.bpm.management.repository.ServiceRegistrationRepository;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.util.List;

/**
 * @author yonghuan
 */
public class ServiceExecutor implements JavaDelegate {

    private final static Logger LOG = LoggerFactory.getLogger(ServiceExecutor.class);

    private ServiceMappingRepository serviceMappingRepository;
    private ProcessDeploymentRepository processDeploymentRepository;
    private ServiceRegistrationRepository serviceRegistrationRepository;
    private ProducerTemplate producerTemplate;
    private ServiceParamMappingRepository serviceParamMappingRepository;
    private ProcessVariableApplication processVariableApplication;
    private ServiceParamApplication serviceParamApplication;

    @Autowired
    public void setServiceMappingRepository(ServiceMappingRepository serviceMappingRepository) {
        this.serviceMappingRepository = serviceMappingRepository;
    }

    @Autowired
    public void setProcessDeploymentRepository(ProcessDeploymentRepository processDeploymentRepository) {
        this.processDeploymentRepository = processDeploymentRepository;
    }

    @Autowired
    public void setServiceRegistrationRepository(ServiceRegistrationRepository serviceRegistrationRepository) {
        this.serviceRegistrationRepository = serviceRegistrationRepository;
    }

    @Produce
    public void setProducerTemplate(ProducerTemplate producerTemplate) {
        this.producerTemplate = producerTemplate;
    }

    @Autowired
    public void setServiceParamMappingRepository(ServiceParamMappingRepository serviceParamMappingRepository) {
        this.serviceParamMappingRepository = serviceParamMappingRepository;
    }

    @Autowired
    public void setProcessVariableApplication(ProcessVariableApplication processVariableApplication) {
        this.processVariableApplication = processVariableApplication;
    }

    @Autowired
    public void setServiceParamApplication(ServiceParamApplication serviceParamApplication) {
        this.serviceParamApplication = serviceParamApplication;
    }

    @Override
    public void execute(DelegateExecution execution) throws Exception {
        ProcessDeployment deployment = processDeploymentRepository.findByActProcdefId(execution.getProcessDefinitionId());
        ServiceMapping serviceMapping = serviceMappingRepository.findByProcdefIdAndActivityCode(deployment.getProcdefId(), execution.getCurrentActivityId());
        if (serviceMapping == null) {
            LOG.warn("流程节点 {} 未绑定业务服务", execution.getCurrentActivityId());
            return;
        }
        ServiceRegistration serviceRegistration = serviceRegistrationRepository.findOne(serviceMapping.getServiceId());
        JSONObject request = new JSONObject();
        JSONObject bizData = new JSONObject();
        request.put("bizData", bizData);
        putVariableToInParam(deployment, serviceRegistration, execution, bizData);
        // 调用业务服务
        request.put("bizKey", execution.getProcessBusinessKey());
        request.put("serviceCode", execution.getCurrentActivityId());
        request.put("sapFunctionName", serviceRegistration.getCode());

        String uri = String.format("%s:%s", serviceRegistration.getKindId(), serviceRegistration.getUri());
        String responseText = (String) producerTemplate.requestBody(uri, request.toJSONString());
        JSONObject response = JSONObject.parseObject(responseText);
        putVariableToOutParam(deployment, serviceRegistration, execution, response);
    }

    private void putVariableToInParam(ProcessDeployment deployment, ServiceRegistration serviceRegistration, DelegateExecution execution, JSONObject request) {
        List<ServiceParamMapping> paramMappings = serviceParamMappingRepository.findAll(deployment.getProcdefId(), execution.getCurrentActivityId());
        for (ServiceParamMapping paramMapping : paramMappings) {
            ServiceParam param = serviceParamApplication.findById(paramMapping.getParamId());
            Assert.notNull(param, String.format("%s服务的参数 %s 不存在", serviceRegistration.getCode(), paramMapping.getParamId()));
            if (ServiceParam.Kind.isOut(param.getKindId())) {
                continue;
            }
            ProcessVariable variable = processVariableApplication.findById(paramMapping.getVariableId());
            Assert.notNull(variable, String.format("流程变量 %s 不存在", paramMapping.getVariableId()));
            Object variableVal = execution.getVariable(variable.getCode());
            if (variableVal != null) {
                request.put(param.getCode(), variableVal);
            }
        }
    }

    private void putVariableToOutParam(ProcessDeployment deployment, ServiceRegistration serviceRegistration, DelegateExecution execution, JSONObject response) {
        List<ServiceParamMapping> paramMappings = serviceParamMappingRepository.findAll(deployment.getProcdefId(), execution.getCurrentActivityId());
        for (ServiceParamMapping paramMapping : paramMappings) {
            ServiceParam param = serviceParamApplication.findById(paramMapping.getParamId());
            Assert.notNull(param, String.format("%s服务的参数 %s 不存在", serviceRegistration.getCode(), paramMapping.getParamId()));
            if (ServiceParam.Kind.isIn(param.getKindId())) {
                continue;
            }
            if (response.containsKey(param.getCode())) {
                ProcessVariable variable = processVariableApplication.findById(paramMapping.getVariableId());
                Assert.notNull(variable, String.format("流程变量 %s 不存在", paramMapping.getVariableId()));
                // 从业务服务的返回参数中获取参数值
                Object paramVal = response.get(param.getCode());
                // 绑定至流程变量
                execution.setVariableLocal(variable.getCode(), paramVal);
            }
        }
    }
}
