package cn.mybatisboost.lang.provider;

import cn.mybatisboost.core.SqlProvider;
import cn.mybatisboost.util.MyBatisUtils;
import cn.mybatisboost.util.SqlUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;

import java.sql.Connection;
import java.util.Iterator;
import java.util.regex.Matcher;

public class NullEnhancement implements SqlProvider {

    @Override
    public void replace(Connection connection, MetaObject metaObject, MappedStatement mappedStatement, BoundSql boundSql) {
        if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT ||
                mappedStatement.getSqlCommandType() == SqlCommandType.DELETE) {
            String sql = boundSql.getSql();

            Matcher matcher = SqlUtils.PATTERN_PLACEHOLDER.matcher(sql);
            Iterator<ParameterMapping> iterator = boundSql.getParameterMappings().iterator();
            MetaObject parameterMetaObject = MyBatisUtils.getMetaObject(boundSql.getParameterObject());
            boolean isUpperCase = Character.isUpperCase(sql.charAt(0));

            int offset = 0;
            StringBuilder sqlBuilder = new StringBuilder();
            while (matcher.find() & iterator.hasNext()) {
                try {
                    if (parameterMetaObject.getValue(iterator.next().getProperty()) != null) continue;
                } catch (Exception ignored) {
                    continue;
                }
                iterator.remove();

                String substring = sql.substring(offset, matcher.end());
                int before = substring.length();
                substring = substring.replaceFirst(" ?!= *\\?$| ?<> *\\?$",
                        isUpperCase ? " IS NOT NULL" : " is not null");
                if (substring.length() == before) {
                    substring = substring.replaceFirst(" ?= *\\?$", isUpperCase ? " IS NULL" : " is null");
                }
                sqlBuilder.append(substring);
                offset = matcher.end();
            }
            sqlBuilder.append(sql, offset, sql.length());
            metaObject.setValue("delegate.boundSql.sql", sqlBuilder.toString());
        }
    }
}