package io.mycat.sqlhandler.dml;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import io.mycat.*;
import io.mycat.calcite.table.SchemaHandler;
import io.mycat.sqlhandler.AbstractSQLHandler;
import io.mycat.sqlhandler.HackRouter;
import io.mycat.sqlhandler.SQLRequest;
import io.mycat.util.NameMap;
import io.mycat.util.Pair;
import lombok.SneakyThrows;

import java.util.Objects;
import java.util.Optional;

public class UpdateSQLHandler extends AbstractSQLHandler<MySqlUpdateStatement> {

    @Override
    protected void onExecute(SQLRequest<MySqlUpdateStatement> request, MycatDataContext dataContext, Response response) throws Exception {
        updateHandler(request.getAst(), dataContext, (SQLExprTableSource) request.getAst().getTableSource(), response);
    }

    @SneakyThrows
    public static void updateHandler(SQLStatement sqlStatement, MycatDataContext dataContext, SQLExprTableSource tableSource, Response receiver) {
        String schemaName = Optional.ofNullable(tableSource.getSchema() == null ? dataContext.getDefaultSchema() : tableSource.getSchema())
                .map(i -> SQLUtils.normalize(i)).orElse(null);
        String tableName = SQLUtils.normalize(tableSource.getTableName());
        SchemaHandler schemaHandler;
        MetadataManager metadataManager = MetaClusterCurrent.wrapper(MetadataManager.class);
        Optional<NameMap<SchemaHandler>> handlerMapOptional = Optional.ofNullable(metadataManager.getSchemaMap());
        Optional<String> targetNameOptional = Optional.ofNullable(metadataManager.getPrototype());
        if (!handlerMapOptional.isPresent()) {
            if (targetNameOptional.isPresent()) {
                receiver.proxyUpdate(targetNameOptional.get(), Objects.toString(sqlStatement));
                return;
            } else {
                receiver.sendError(new MycatException("Unable to route:" + sqlStatement));
                return;
            }
        } else {
            NameMap< SchemaHandler> handlerMap = handlerMapOptional.get();
            schemaHandler = Optional.ofNullable(handlerMap.get(schemaName))
                    .orElseGet(() -> {
                        if (dataContext.getDefaultSchema() == null) {
                            throw new MycatException("unknown schema:" + schemaName);//可能schemaName有值,但是值名不是配置的名字
                        }
                        return handlerMap.get(dataContext.getDefaultSchema());
                    });
            if (schemaHandler == null) {
                receiver.sendError(new MycatException("Unable to route:" + sqlStatement));
                return;
            }
        }
        String defaultTargetName = schemaHandler.defaultTargetName();
        NameMap< TableHandler> tableMap = schemaHandler.logicTables();
        TableHandler tableHandler = tableMap.get(tableName);
        ///////////////////////////////common///////////////////////////////
        if (tableHandler == null) {
            receiver.proxyUpdate(defaultTargetName, sqlStatement.toString());
            return;
        }
        switch (tableHandler.getType()) {
            case NORMAL:
                HackRouter hackRouter = new HackRouter(sqlStatement, dataContext);
                if(hackRouter.analyse()){
                    Pair<String, String> plan = hackRouter.getPlan();
                    receiver.proxyUpdate(plan.getKey(),plan.getValue());
                    return;
                }
                break;
            default:
                break;
        }
        DrdsRunner drdsRunner = MetaClusterCurrent.wrapper(DrdsRunner.class);
        drdsRunner.runOnDrds(dataContext,sqlStatement,receiver);
    }
}
