/*
 * Copyright 2020. the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package group.idealworld.dew.core.cluster.ha;

import com.ecfront.dew.common.$;
import group.idealworld.dew.core.cluster.dto.MessageWrap;
import group.idealworld.dew.core.cluster.ha.dto.HAConfig;
import group.idealworld.dew.core.cluster.ha.entity.PrepareCommitMsg;
import org.h2.jdbcx.JdbcConnectionPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 集群HA处理 H2 实现.
 *
 * @author gudaoxuri
 */
public class H2ClusterHA implements ClusterHA {

    private static final Logger logger = LoggerFactory.getLogger(H2ClusterHA.class);

    private static JdbcConnectionPool jdbcConnectionPool;

    private static boolean update(String sql, Object... params) throws SQLException {
        try (Connection conn = jdbcConnectionPool.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            for (int i = 1; i <= params.length; i++) {
                stmt.setObject(i, params[i - 1]);
            }
            return stmt.execute();
        }
    }

    private static List<PrepareCommitMsg> queryList(String sql, Object... params) throws SQLException {
        ResultSet rs = null;
        try (Connection conn = jdbcConnectionPool.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            for (int i = 1; i <= params.length; i++) {
                stmt.setObject(i, params[i - 1]);
            }
            rs = stmt.executeQuery();
            return convertResultSetToJob(rs);
        } finally {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }
        }
    }

    private static List<PrepareCommitMsg> convertResultSetToJob(ResultSet rs) throws SQLException {
        if (rs == null) {
            return null;
        }
        List<PrepareCommitMsg> jobs = new ArrayList<>();
        while (rs.next()) {
            PrepareCommitMsg prepareCommitMsg = new PrepareCommitMsg();
            prepareCommitMsg.setAddr(rs.getString(1));
            prepareCommitMsg.setMsgId(rs.getString(2));
            prepareCommitMsg.setMsg($.json.toObject(rs.getString(3), MessageWrap.class));
            prepareCommitMsg.setCreatedTime(rs.getDate(4));
            jobs.add(prepareCommitMsg);
        }
        return jobs;
    }

    @Override
    public void init(HAConfig haConfig) throws SQLException {
        String url = "jdbc:h2:" + haConfig.getStoragePath() + haConfig.getStorageName() + ";DB_CLOSE_ON_EXIT=FALSE";
        jdbcConnectionPool = JdbcConnectionPool
                .create(url,
                        haConfig.getAuthUsername() == null ? "" : haConfig.getAuthUsername(),
                        haConfig.getAuthPassword() == null ? "" : haConfig.getAuthPassword());
        try (Connection conn = jdbcConnectionPool.getConnection(); Statement stmt = conn.createStatement()) {
            stmt.execute("CREATE TABLE IF NOT EXISTS MQ_MSG("
                    + "ADDR VARCHAR(1024),"
                    + "MSG_ID VARCHAR(32),"
                    + "MSG TEXT,"
                    + "CREATED_TIME TIMESTAMP ,"
                    + "PRIMARY KEY(MSG_ID)"
                    + ")");
        }
    }

    @Override
    public String mq_afterPollMsg(String addr, MessageWrap msg) {
        String sql = "INSERT INTO MQ_MSG VALUES(?,?,?,?)";
        Date date = new Date(System.currentTimeMillis());
        try {
            String uuid = $.field.createUUID();
            update(sql, addr, uuid, $.json.toJsonString(msg), date);
            return uuid;
        } catch (SQLException e) {
            logger.error("Create HA job error.", e);
            return "0";
        }
    }

    @Override
    public void mq_afterMsgAcked(String id) {
        String sql = "DELETE FROM MQ_MSG WHERE MSG_ID = ?";
        try {
            update(sql, id);
        } catch (SQLException e) {
            logger.error("Delete HA job error.", e);
        }
    }

    @Override
    public List<PrepareCommitMsg> mq_findAllUnCommittedMsg(String addr) {
        String sql = "SELECT * FROM MQ_MSG where ADDR = ? ORDER BY CREATED_TIME DESC";
        try {
            return queryList(sql, addr);
        } catch (SQLException e) {
            logger.error("Query HA job error.", e);
            return new ArrayList<>();
        }
    }

}