package cn.axzo.msg.center.message.service.todo.mybatis;

import com.mysql.cj.jdbc.ClientPreparedStatement;
import com.zaxxer.hikari.pool.ProxyStatement;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
import org.apache.ibatis.plugin.Invocation;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @author yanglin
 */
@Slf4j
class SQLAccessor {

    /**
     * exception free
     */
    static String getSQL(Invocation invocation) {
        try {
            return getSQLImpl(invocation.getArgs()[0]);
        } catch (SQLException e) {
            log.warn("Error getting SQL", e);
            return null;
        }
    }

    @Nullable
    private static String getSQLImpl(Object arg) throws SQLException {
        Statement stmt = null;
        // logger is enabled
        if (Proxy.isProxyClass(arg.getClass())) {
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(arg);
            if (!(invocationHandler instanceof PreparedStatementLogger))
                return null;
            Statement proxiedStmt = ((PreparedStatementLogger) invocationHandler).getPreparedStatement();
            if (proxiedStmt instanceof ProxyStatement)
                stmt = getStmtFromProxy((ProxyStatement) proxiedStmt);
        } else if (arg instanceof ProxyStatement) {
            stmt = getStmtFromProxy((ProxyStatement) arg);
        }
        if (stmt == null)
            return null;
        if (!(stmt instanceof ClientPreparedStatement))
            return null;
        return ((ClientPreparedStatement) stmt).asSql();
    }

    public static String getSql(ProxyStatement proxy) throws Exception {
        Statement stmt = getStmtFromProxy(proxy);
        if (stmt == null)
            return null;
        if (!(stmt instanceof ClientPreparedStatement))
            return null;
        return ((ClientPreparedStatement) stmt).asSql();
    }

    private static Statement getStmtFromProxy(ProxyStatement proxy) {
        Field delegateField = ReflectionUtils.findField(proxy.getClass(), "delegate");
        if (delegateField == null) return null;
        delegateField.setAccessible(true);
        return (Statement) ReflectionUtils.getField(delegateField, proxy);
    }

}
