package com.huigou.uasp.bmp.operator.impl;

import com.alibaba.fastjson.JSON;
import com.huigou.data.domain.model.AbstractEntity;
import com.huigou.data.repository.GeneralRepository;
import com.huigou.uasp.bmp.operator.OperatorUIElementPermissionBuilder;
import com.huigou.uasp.bmp.opm.proxy.AccessApplicationProxy;
import com.huigou.uasp.bpm.CooperationModelKind;
import com.huigou.uasp.bpm.engine.application.ProcUnitHandlerApplication;
import com.huigou.uasp.bpm.engine.application.WorkflowApplication;
import com.huigou.uasp.bpm.engine.domain.model.ProcUnitHandler;
import com.huigou.uasp.bpm.engine.domain.query.TaskDetail;
import com.huigou.util.StringUtil;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.TaskInfo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 界面元素权限查询
 *
 * @author xx
 */

@Service("operatorUIElementPermissionBuilder")
public class OperatorUIElementPermissionBuilderImp implements OperatorUIElementPermissionBuilder {

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

    @Autowired
    private AccessApplicationProxy accessApplication;
    @Autowired
    protected WorkflowApplication workflowApplication;
    @Autowired
    private GeneralRepository generalRepository;
    @Autowired
    private ProcUnitHandlerApplication procUnitHandlerApplication;

    @Override
    public List<Map<String, Object>> queryUIElementPermissionsByFunction(String function, String personId, boolean isId) {
        if (personId == null) {
            return Collections.emptyList();
        }
        return accessApplication.queryUIElementPermissionsByFunction(function, personId, isId);
    }

    @Override
    public List<Map<String, Object>> queryUIElementPermissionsByProcUnitHandlerId(String id) {
        if (StringUtil.isBlank(id)) {
            return Collections.emptyList();
        }
        return workflowApplication.queryUIElmentPermissionsByProcUnitHandlerId(id);
    }

    @Override
    public List<Map<String, Object>> queryUIElementPermissionsByTaskId(String taskId) {
        TaskDetail taskDetail = workflowApplication.queryTaskDetail(taskId);
        if (taskDetail == null) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> flowElementFieldPermissions = getFlowElementFieldPermissions(taskId);
        // 找出审批规则配置的字段权限
        List<Map<String, Object>> uiElementPermissions = this.queryUIElementPermissionsByProcUnitHandlerId(taskDetail.getProcUnitHandlerId());
        if (uiElementPermissions.size() > 0) {
            return mergeFieldPermissions(flowElementFieldPermissions, uiElementPermissions);
        } else {
            String chiefHandlerId = taskDetail.getProcUnitHandlerId();
            // 协审人的字段权限与主审人保存一致
            ProcUnitHandler procUnitHandler = null;
            if (chiefHandlerId != null) {
                procUnitHandler = procUnitHandlerApplication.loadProcUnitHandler(chiefHandlerId);
                if (procUnitHandler != null && CooperationModelKind.ASSISTANT.equals(procUnitHandler.getCooperationModelId())) {
                    chiefHandlerId = procUnitHandler.getChiefId();
                    procUnitHandler = procUnitHandlerApplication.loadProcUnitHandler(chiefHandlerId);
                }
            }
            String approvalRuleHandlerId = procUnitHandler != null ? procUnitHandler.getApprovalRuleHandlerId() : null;
            flowElementFieldPermissions.forEach(it -> it.put("approvalRuleHandlerId", approvalRuleHandlerId));
            return flowElementFieldPermissions;
        }
    }

    /**
     * 找出流程图中配置的表单字段权限
     *
     * @param taskId 任务id
     */
    private List<Map<String, Object>> getFlowElementFieldPermissions(String taskId) {
        TaskInfo task = workflowApplication.getTaskService().createTaskQuery()
                .taskId(taskId)
                .singleResult();
        if (task == null) {
            task = workflowApplication.getHistoryService().createHistoricTaskInstanceQuery()
                    .taskId(taskId)
                    .singleResult();
        }
        BpmnModel bpmnModel = getBpmnModelByProcessInstanceId(task.getProcessInstanceId());
        if (bpmnModel == null) {
            // 未找到任务对应的BpmnModel时直接返回空集合，不影响业务的正常流转
            LOG.error(String.format("未找到任务[taskId=%s]对应的BpmnModel", taskId));
            return Collections.emptyList();
        }
        Process process = bpmnModel.getProcesses().get(0);
        FlowElement fe = process.getFlowElement(task.getTaskDefinitionKey());
        List<Map<String, Object>> flowElementFieldPermissions = getFieldExtensionElement(process, "formNo")
                .map(ExtensionAttribute::getValue)
                .map(formNo -> getFlowElementFieldPermissions(formNo, fe))
                .orElseGet(Collections::emptyList);
        flowElementFieldPermissions.forEach(flowElementFieldPermission ->
                getFormFieldPermissionKindId((String) flowElementFieldPermission.get("formFieldId"))
                        .ifPresent(kindId -> flowElementFieldPermission.put("kindId", kindId))
        );
        return flowElementFieldPermissions;
    }

    /**
     * 找出流程图中配置的表单字段权限
     *
     * @param formNo 绑定的表单编号
     * @param fe     流程图中的节点
     * @return 流程图中节点的表单字段权限
     */
    private List<Map<String, Object>> getFlowElementFieldPermissions(String formNo, BaseElement fe) {
        return getFieldExtensionElement(fe, "uiElementPermission")
                .map(ExtensionAttribute::getValue)
                .map(JSON::parseObject)
                .map(flowElementFieldPermission -> (Map<String, Object>) flowElementFieldPermission)
                .orElseGet(Collections::emptyMap)
                .entrySet()
                .stream()
                .filter(flowElementFieldPermission -> StringUtils.isNotBlank((String) flowElementFieldPermission.getValue()))
                .flatMap(flowElementFieldPermission -> ((List<? extends AbstractEntity>) generalRepository.getEntityManager()
                        .createQuery("select fa from FormAttribute fa inner join SafFormslist f on fa.formId=f.id where f.formNo=:formNo and fa.attrCode=:fieldName")
                        .setParameter("formNo", formNo)
                        .setParameter("fieldName", flowElementFieldPermission.getKey())
                        .getResultList())
                        .stream()
                        .map(formField -> {
                            Map<String, Object> permission = new HashMap<>();
                            permission.put("operationId", flowElementFieldPermission.getValue());
                            permission.put("code", flowElementFieldPermission.getKey());
                            permission.put("formFieldId", formField.getId());
                            return permission;
                        }))
                .collect(Collectors.toList());
    }

    private BpmnModel getBpmnModelByProcessInstanceId(String processInstanceId) {
        if (processInstanceId == null) {
            return null;
        }
        String deploymentId = null;
        ProcessInstance pi = workflowApplication.getRunTimeService().createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if (pi != null) {
            deploymentId = pi.getDeploymentId();
        } else {
            HistoricProcessInstance historicProcessInstance = workflowApplication.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
                    .singleResult();
            if (historicProcessInstance != null) {
                deploymentId = historicProcessInstance.getDeploymentId();
            }
        }
        BpmnModel bpmnModel = null;
        if (deploymentId != null) {
            Deployment deployment = workflowApplication.getRepositoryService().createDeploymentQuery()
                    .deploymentId(deploymentId)
                    .singleResult();
            ProcessDefinition pd = workflowApplication.getRepositoryService().createProcessDefinitionQuery()
                    .deploymentId(deployment.getId())
                    .singleResult();
            bpmnModel = workflowApplication.getRepositoryService().getBpmnModel(pd.getId());
        }
        return bpmnModel;
    }

    /**
     * 合并字段权限
     *
     * @param flowElementFieldPermissions 流程图中配置的表单字段权限
     * @param uiElementPermissions        审批规则中配置的字段权限
     */
    private List<Map<String, Object>> mergeFieldPermissions(List<Map<String, Object>> flowElementFieldPermissions, List<Map<String, Object>> uiElementPermissions) {
        List<Map<String, Object>> mergeResult = new ArrayList<>(16);
        // 先将流程图中配置的字段权限放入合并结果集中
        mergeResult.addAll(flowElementFieldPermissions);
        // 遍历审批规则中配置的字段权限，并在合并结果集中查询有相同formFieldId的配置，如果能找到就覆盖找到的配置，否则添加进合并结果集
        for (Map<String, Object> o : uiElementPermissions) {
            String formFieldId = (String) o.get("formFieldId");
            if (StringUtils.isBlank(formFieldId)) {
                // 不是关联的表单字段，直接添加进合并结果集
                mergeResult.add(o);
            }
            getFormFieldPermissionKindId(formFieldId).ifPresent(kindId -> o.put("kindId", kindId));
            Optional<Map<String, Object>> uiElementPermissionOptional = mergeResult.stream().filter(it -> it.get("formFieldId").equals(formFieldId)).findAny();
            if (uiElementPermissionOptional.isPresent()) {
                Map<String, Object> uiElementPermission = uiElementPermissionOptional.get();
                uiElementPermission.putAll(o);
            } else {
                mergeResult.add(o);
            }
        }
        return mergeResult;
    }

    private Optional<Integer> getFormFieldPermissionKindId(String formFieldId) {
        return ((List<Integer>) generalRepository.getEntityManager().createQuery("select fa.tableType from FormAttribute fa where fa.id=:formFieldId")
                .setParameter("formFieldId", formFieldId)
                .getResultList())
                .stream()
                .findAny()
                .map(tableType -> tableType.intValue() == 1 ? 0 : 1);
    }

    private Optional<ExtensionAttribute> getFieldExtensionElement(BaseElement be, String fieldName) {
        return be.getExtensionElements().getOrDefault("field", Collections.emptyList())
                .stream()
                .flatMap(field -> field.getAttributes().getOrDefault(fieldName, Collections.emptyList()).stream())
                .findAny()
                .filter(attr -> StringUtils.isNotBlank(attr.getValue()));
    }

}
