package com.bkjk.platfrom.dts.core.resource.mysql.rollback;

import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.bkjk.platfrom.dts.core.resource.mysql.DbRuntimeContext.CommitInfo;
import com.bkjk.platfrom.dts.core.resource.mysql.common.*;
import com.bkjk.platfrom.dts.core.resource.mysql.common.TableDataInfo.TxcLine;
import com.bkjk.platfrom.dts.core.resource.mysql.common.TableDataInfo.TxcLine.TxcField;
import com.bkjk.platfrom.dts.core.resource.mysql.parser.UpdateParser;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class UpdateRollback extends AbstractRollback {
    private static UpdateRollback instance = null;

    public static UpdateRollback getInstance() {
        if (instance == null) {
            synchronized (UpdateRollback.class) {
                if (instance == null) {
                    instance = new UpdateRollback();
                }
            }
        }
        return instance;
    }

    private Logger logger = LoggerFactory.getLogger(UpdateRollback.class);

    @Override
    protected List<PreparedStatement> assembleRollbackSql(CommitInfo commitInfo, Connection connection)
        throws SQLException {
        ArrayList<PreparedStatement> preparedStatements = Lists.newArrayList();
        String tableName = commitInfo.getOriginalValue().getTableName();
        for (TxcLine txcLine : commitInfo.getOriginalValue().getLine()) {
            List<TxcField> txcFields = txcLine.getFields();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("update ").append(tableName).append(" ").append("set ");
            for (int i = 0; i < txcFields.size(); i++) {
                if (i == txcFields.size() - 1) {
                    stringBuilder.append(txcFields.get(i).getSqlName()).append("=").append("?");
                } else {
                    stringBuilder.append(txcFields.get(i).getSqlName()).append("=").append("?").append(",");
                }
            }
            String sql = stringBuilder.append(" where ").append(txcLine.getPkCondition()).toString();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int j = 1; j <= txcFields.size(); j++) {
                preparedStatement.setObject(j, SerializeUtils.derialize(txcFields.get(j - 1).getJdkValue()));
            }
            for (PkPair<String, Object> primaryKeyValue : txcLine.getPrimaryKeyValues()) {
                preparedStatement.setObject(
                    txcLine.getPrimaryKeyValues().indexOf(primaryKeyValue) + 1 + txcFields.size(),
                    primaryKeyValue.getValue());
            }
            preparedStatements.add(preparedStatement);
        }
        return preparedStatements;

    }

    @Override
    protected boolean canRollback(CommitInfo commitInfo, Connection connection) throws SQLException {
        String sql = commitInfo.getSql();
        SQLUpdateStatement sqlParseStatement = (SQLUpdateStatement)new MySqlStatementParser(sql).parseStatement();
        TableMetaInfo tableMetaInfo =
            TableMetaUtils.getTableMetaInfo(connection, sqlParseStatement.getTableName().getSimpleName());
        TableDataInfo dbValue = UpdateParser.getInstance().getOriginValue(commitInfo.getWhereParams(),
            sqlParseStatement, connection, tableMetaInfo);
        if (commitInfo.getOriginalValue().getLine().size() == 0) {
            return false;
        }
        for (TxcLine txcLine : dbValue.getLine()) {
            txcLine.setPrimaryKeyValues(commitInfo.getPresentValue().getLine().get(0).getPrimaryKeyValues());
            boolean diff = DiffUtils.diff(commitInfo.getPresentValue().getLine().get(0), txcLine);
            if (!diff) {
                try {
                    logger.error("data conflict, before:{},after:{}",
                        DiffUtils.getObjectMapper().writeValueAsString(commitInfo.getPresentValue().getLine().get(0)),
                        DiffUtils.getObjectMapper().writeValueAsString(txcLine));
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
                return false;
            }
        }
        return true;
    }

}