package cn.mybatisboost.core;

import cn.mybatisboost.util.MyBatisUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;

import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;

@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}))
public class DispatcherInterceptor implements Interceptor {

    private Configuration configuration;
    private List<SqlProvider> preprocessors = new CopyOnWriteArrayList<>();
    private List<SqlProvider> providers = new CopyOnWriteArrayList<>();

    public DispatcherInterceptor(Configuration configuration) {
        this.configuration = configuration;
    }

    public void appendPreprocessor(SqlProvider provider) {
        preprocessors.add(provider);
        if (provider instanceof ConfigurationAware) {
            ((ConfigurationAware) provider).setConfiguration(configuration);
        }
    }

    public void appendProvider(SqlProvider provider) {
        providers.add(provider);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Connection connection = (Connection) invocation.getArgs()[0];
        MetaObject metaObject = MyBatisUtils.getMetaObject(invocation.getTarget());
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        preprocessors.forEach(p -> p.replace(connection, metaObject, mappedStatement, boundSql));
        providers.forEach(p -> p.replace(connection, metaObject, mappedStatement, boundSql));
        return invocation.proceed();
    }

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

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