package cn.axzo.workflow.core.engine.cmd;

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException;
import org.apache.ibatis.exceptions.PersistenceException;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableOptimisticLockingException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Objects;

/**
 * 对 CommandContext 中的 WorkflowEngineException 进行日志降级
 *
 * @author wangli
 * @since 2024/5/21 09:46
 */
public class CustomCommandContext extends CommandContext {
    private static final Logger LOGGER = LoggerFactory.getLogger(CustomCommandContext.class);

    private static final String[] PERSISTENCE_EXCEPTION_WARN_MESSAGE = new String[]{"act_ru_job"};

    public CustomCommandContext(Command<?> command) {
        super(command);
    }

    @Override
    protected void logException() {
        if (exception instanceof FlowableException && !((FlowableException) exception).isLogged()) {
            return;
        }

        if (exception instanceof FlowableOptimisticLockingException) {
            // reduce log level, as normally we're not interested in logging this exception
            LOGGER.debug("Optimistic locking exception : {}", exception.getMessage(), exception);

        } else if (exception instanceof FlowableException && ((FlowableException) exception).isReduceLogLevel()) {
            // reduce log level, because this may have been caused because of job deletion due to cancelActiviti="true"
            LOGGER.info("Error while closing command context", exception);
        } else if (exception instanceof WorkflowEngineException) {
            LOGGER.warn("Workflow error while closing command context", exception);
        } else if (exception instanceof PersistenceException) {
            Throwable rootCause = getRootCause(exception);
            if (rootCause instanceof MySQLTransactionRollbackException) {
                LOGGER.warn("MySQL transaction rollback exception : {}", rootCause.getMessage(), rootCause);
            } if (rootCause instanceof SQLIntegrityConstraintViolationException){
                LOGGER.warn("SQL constraint violation exception : {}", rootCause.getMessage(), rootCause);
            } else {
                LOGGER.warn("persistence error while closing command context:{}", exception.getMessage(), exception);
            }
        } else {
            LOGGER.error("Error while closing command context", exception);
        }
    }

    private Throwable getRootCause(Throwable throwable) {
        while (Objects.nonNull(throwable.getCause())) {
            throwable = throwable.getCause();
        }
        return throwable;
    }
}
