package com.ruoyi.framework.aspectj;


import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.tenantdatassource.TenantDataSource;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

/**
 * AOP 拦截SQL处理
 *
 * @author hzh
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable, TenantDataSource {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        String sql = statementHandler.getBoundSql().getSql();
        // SQL执行前
        // String modifiedSql = BeforeSQL(sql);
        // 拼接sql
        String modifiedSql = modifySql(sql);


        if (modifiedSql.equals("error")) {
            // 终止程序
            return AjaxResult.error("当前用户没有数据源信息！");
        } else {
            // 将修改后的SQL语句设置回StatementHandler
//            ReflectUtils.setFieldValue(statementHandler.getBoundSql(), "sql", modifiedSql);
//            invocation.proceed();
//            ReflectUtils.setFieldValue(statementHandler.getBoundSql(), "sql", sql);
//            return invocation.proceed();


            // 将修改后的SQL语句设置回StatementHandler
            ReflectUtils.setFieldValue(statementHandler.getBoundSql(), "sql", modifiedSql);
            return invocation.proceed();
        }
    }

    // 根据类型设置不同的选择数据源格式
    private String modifySql(String sql) {
        try {
            SecurityUtils.getDatabaseType();
        } catch (Exception e) {
            return "error";
        }
        if (SecurityUtils.getDatabaseType().equals("sqlserver"))
            return "USE `" + SecurityUtils.getDatabaseName() + "`; " + sql;
        if (SecurityUtils.getDatabaseType().equals("dm"))
            return "set schema " + SecurityUtils.getDatabaseName() + "; " + sql;
        if (SecurityUtils.getDatabaseType().equals("mysql")) {
            if (sql.contains("{DBNAME}.")) {
                return sql.replace("{DBNAME}.", "`" + SecurityUtils.getDatabaseName() + "`.");
            } else {
                if (sql.contains("information_schema")) return sql;  // 执行当前sql不需要选择数据源
                StringBuilder sb = new StringBuilder(sql);
                if (sb.indexOf(" from ") != -1) {
                    sb.insert(sb.indexOf(" from ") + 6, "`" + SecurityUtils.getDatabaseName() + "`.");  // 在目标字符后插入新的字符串
                } else if (sb.indexOf(" FROM ") != -1) {
                    sb.insert(sb.indexOf(" FROM ") + 6, "`" + SecurityUtils.getDatabaseName() + "`.");  // 在目标字符后插入新的字符串
                } else if (sb.indexOf(" into ") != -1) {
                    sb.insert(sb.indexOf(" into ") + 6, "`" + SecurityUtils.getDatabaseName() + "`.");  // 在目标字符后插入新的字符串
                } else if (sb.indexOf("update ") != -1) {
                    sb.insert(sb.indexOf("update ") + 7, "`" + SecurityUtils.getDatabaseName() + "`.");  // 在目标字符后插入新的字符串
                }
                return sb.toString();
            }
        }
        return "error";
    }

    // 根据类型设置不同的选择数据源格式
    private String BeforeSQL(String sql) {
        try {
            SecurityUtils.getDatabaseType();
        } catch (Exception e) {
            /**
             * 如何当前请求中并不携带该用户的数据源信息
             * 分两种情况：
             * 1.当前用户并没有数据源信息（例如：admin并没有从库数据源信息）
             * 2.当前用户的token失效（理论上在客户端发送请求时会携带token，逻辑代码执行前会首先验证token的有效性）
             * 几乎不会出现第二种情况
             */
            return "error";
        }
        if (SecurityUtils.getDatabaseType().equals("sqlserver"))
            return "USE `" + SecurityUtils.getDatabaseName() + "`; ";
        if (SecurityUtils.getDatabaseType().equals("dm"))
            return "set schema " + SecurityUtils.getDatabaseName() + "; ";
        if (SecurityUtils.getDatabaseType().equals("mysql"))
            return "USE `" + SecurityUtils.getDatabaseName() + "`; ";
        return "error";
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}