package cn.stylefeng.workflow.instance.core.actions.gateway;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.sys.api.SysUserOrgServiceApi;
import cn.stylefeng.roses.kernel.sys.api.SysUserRoleServiceApi;
import cn.stylefeng.roses.kernel.sys.api.pojo.user.UserOrgDTO;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNode;
import cn.stylefeng.workflow.api.enums.CommonVariableEnum;
import cn.stylefeng.workflow.api.enums.NodeStatusEnum;
import cn.stylefeng.workflow.api.enums.NodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.InstanceVariable;
import cn.stylefeng.workflow.api.pojo.config.node.condition.NodeCondition;
import cn.stylefeng.workflow.design.api.NodeConditionServiceApi;
import cn.stylefeng.workflow.instance.api.factory.RuntimeNodeFactory;
import cn.stylefeng.workflow.instance.api.node.NodeAction;
import cn.stylefeng.workflow.instance.api.pojo.NodeActionResult;
import cn.stylefeng.workflow.instance.core.path.NodePathCalc;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeNodeService;
import cn.stylefeng.workflow.log.api.WorkflowLogApi;
import cn.stylefeng.workflow.rule.api.ExpressionEngineApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class ConditionNodeAction extends NodeAction {

    @Resource
    private NodeConditionServiceApi nodeConditionServiceApi;

    @Resource
    private WorkflowLogApi workflowLogApi;

    @Resource
    private RuntimeNodeService runtimeNodeService;

    @Resource
    private ExpressionEngineApi expressionEngineApi;

    @Resource
    private SysUserOrgServiceApi sysUserOrgServiceApi;

    @Resource
    private SysUserRoleServiceApi sysUserRoleServiceApi;

    @Override
    public NodeTypeEnum getNodeType() {
        return NodeTypeEnum.CONDITION_GATEWAY_CONDITION;
    }

    @Override
    public NodeActionResult doAction(Node nodeInfo, InstanceContext instanceContext) {

        NodeCondition nodeCondition = nodeConditionServiceApi.getNodeCondition(nodeInfo.getNodeId());

        if (nodeCondition == null) {
            workflowLogApi.logSuccess(instanceContext, nodeInfo.getNodeId(), "节点无法找到节点组信息");
            return new NodeActionResult(null, null, instanceContext.getInstanceVariable());
        }

        String expressionString = nodeCondition.getExpressionString();
        if (ObjectUtil.isEmpty(expressionString)) {
            workflowLogApi.logSuccess(instanceContext, nodeInfo.getNodeId(), "节点组绑定的条件表达式为空");
            return new NodeActionResult(null, null, instanceContext.getInstanceVariable());
        }

        Long applicant = null;
        if (instanceContext.getRuntimeInstance() != null) {
            applicant = instanceContext.getRuntimeInstance().getApplicant();
        }
        boolean calcResult = this.commonConditionRuleResult(expressionString, applicant, instanceContext.getInstanceVariable());

        if (calcResult) {
            workflowLogApi.logSuccess(instanceContext, nodeInfo.getNodeId(), "条件成立，继续往下执行本分支");

            Long instanceId = instanceContext.getRuntimeInstance().getInstanceId();
            RuntimeNode runtimeNode = RuntimeNodeFactory.initRuntimeNode(nodeInfo, instanceId);

            runtimeNode.setNodeStatus(NodeStatusEnum.FINISH.getStatus());
            this.runtimeNodeService.save(runtimeNode);

            List<Node> nextNodeList = new NodePathCalc(instanceContext).calcNextNodeList(nodeInfo.getNodeId());
            return new NodeActionResult(runtimeNode, nextNodeList, instanceContext.getInstanceVariable());
        }

        else {

            workflowLogApi.logSuccess(instanceContext, nodeInfo.getNodeId(), "条件不成立，则不继续执行这个分支");

            return new NodeActionResult(null, null, instanceContext.getInstanceVariable());
        }
    }

    public boolean commonConditionRuleResult(String originExpression, Long applicant, InstanceVariable variableObject) {

        Map<String, Object> expressionVariables = new HashMap<>();

        this.fillCommonVariable(originExpression, expressionVariables, applicant);

        this.fillFormVariables(originExpression, expressionVariables, variableObject);

        return expressionEngineApi.parseExpression(originExpression, expressionVariables);
    }

    private void fillCommonVariable(String originExpression, Map<String, Object> variables, Long applicant) {

        if (ObjectUtil.isEmpty(applicant)) {
            return;
        }

        if (originExpression.contains(CommonVariableEnum.APPLICANT.getExpressionCode())) {
            variables.put(CommonVariableEnum.APPLICANT.getVariableCode(), String.valueOf(applicant));
        }

        if (originExpression.contains(CommonVariableEnum.DEPT.getExpressionCode())) {

            UserOrgDTO userMainOrgInfo = sysUserOrgServiceApi.getUserMainOrgInfo(applicant);
            Long deptId = userMainOrgInfo.getDeptId();
            if (deptId == null) {
                deptId = userMainOrgInfo.getCompanyId();
            }
            variables.put(CommonVariableEnum.DEPT.getVariableCode(), String.valueOf(deptId));
        }

        if (originExpression.contains(CommonVariableEnum.ROLE.getExpressionCode())) {

            List<Long> userRoleIdList = sysUserRoleServiceApi.getUserRoleIdList(applicant);
            if (ObjectUtil.isNotEmpty(userRoleIdList)) {
                Long userRoleId = userRoleIdList.get(0);
                variables.put(CommonVariableEnum.ROLE.getVariableCode(), String.valueOf(userRoleId));
            }
        }

        if (originExpression.contains(CommonVariableEnum.POSITION.getExpressionCode())) {

            UserOrgDTO userMainOrgInfo = sysUserOrgServiceApi.getUserMainOrgInfo(applicant);
            Long positionId = userMainOrgInfo.getPositionId();
            if (ObjectUtil.isNotEmpty(positionId)) {
                variables.put(CommonVariableEnum.POSITION.getVariableCode(), String.valueOf(positionId));
            }
        }
    }

    private void fillFormVariables(String processedExpression, Map<String, Object> variables, InstanceVariable variableObject) {

        if (ObjectUtil.isEmpty(variableObject)) {
            return;
        }

        Map<String, Object> customVueFormData = variableObject.getCustomVueFormData();
        findVariablesFromFormDataMap(processedExpression, variables, customVueFormData);

        Map<String, Map<String, Object>> formTotalData = variableObject.getFormData();

        if (formTotalData != null) {
            Set<String> formDefinitionIdList = formTotalData.keySet();

            for (String formDefinitionId : formDefinitionIdList) {

                Map<String, Object> formData = formTotalData.get(formDefinitionId);
                findVariablesFromFormDataMap(processedExpression, variables, formData);
            }
        }
    }

    private void findVariablesFromFormDataMap(String processedExpression, Map<String, Object> variables, Map<String, Object> formData) {
        if (ObjectUtil.isEmpty(formData)) {
            return;
        }

        Dict formDataDict = Dict.create();
        formDataDict.putAll(formData);

        for (String fieldName : formData.keySet()) {
            if (processedExpression.contains("#" + fieldName)) {

                String variableValue = formDataDict.getStr(fieldName);

                Date dateValue = dateFlag(variableValue);
                boolean numberFlag = numberFlag(variableValue);
                if (dateValue != null) {
                    variables.put(fieldName, DateUtil.formatDateTime(dateValue));
                }

                else if (numberFlag) {
                    BigDecimal bigDecimal = new BigDecimal(variableValue);
                    variables.put(fieldName, bigDecimal);
                }

                else {
                    variables.put(fieldName, variableValue);
                }
            }
        }
    }

    private Date dateFlag(String dateStr) {
        try {

            return DateUtil.parseDateTime(dateStr);
        } catch (Exception e) {
            try {

                return DateUtil.parseDate(dateStr);
            } catch (Exception ex) {
                return null;
            }
        }
    }

    private boolean numberFlag(String numberStr) {
        return NumberUtil.isNumber(numberStr) || NumberUtil.isDouble(numberStr);
    }

}
