package leigh.esp.goryu.engine;

import leigh.eo.EO;
import leigh.eo.io.EOInputStream;
import leigh.eo.io.EOOutputStream;
import leigh.esp.goryu.sdk.schema.v1.MutationElementDAO;
import leigh.esp.goryu.sdk.schema.v1.QueryElementDAO;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.io.IOException;
import java.io.Serializable;
import java.sql.*;
import java.util.LinkedList;

/**
 * The Goryu transaction processor. This class provides the mechanics for reading a stream of request objects
 * to perform a database transaction.
 *
 * @author Alex Leigh
 */
public class TransactionProcessor {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(TransactionProcessor.class);

    private void mutate(Connection con, EO obj) throws SQLException {
        String text = MutationElementDAO.getQuery(obj);
        logger.info("Running mutation: {}", text);
        try (PreparedStatement ps = con.prepareStatement(text)) {
            ps.execute();
        }
    }

    /**
     * Process a transaction.
     *
     * @param eis
     * @param eos
     */
    public void process(Connection con, EOInputStream eis, EOOutputStream eos) throws IOException, SQLException {
        LinkedList<EO> queries = new LinkedList<>();

        while (true) {
            final EO obj = eis.readEo();
            if (obj == null) break;

            logger.info("Received object: {}", obj);

            switch (obj.getApiType()) {
                case QueryElementDAO.API_TYPE:
                    queries.add(obj);
                    continue;
                case MutationElementDAO.API_TYPE:
                    mutate(con, obj);
                    continue;
                default:
                    throw new IllegalArgumentException("Unknown request object: " + obj.getApiType());
            }
        }

        logger.info("Mutations: {}", queries);

        for (EO query : queries) {
            String queryText = QueryElementDAO.getQuery(query);
            try (PreparedStatement ps = con.prepareStatement(queryText)) {
                try (ResultSet rs = ps.executeQuery()) {
                    streamResultSet(eos, rs);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void streamResultSet(EOOutputStream os, ResultSet rs) throws Exception {
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnsNumber = rsmd.getColumnCount();

        EO obj = new EO();

        while (rs.next()) {
            obj.clear();
            for (int i = 1; i <= columnsNumber; i++) {
                // Frankly this is a dubious premise.
                obj.setValue(rsmd.getColumnName(i), (Serializable) rs.getObject(i));
            }
            os.writeEO(obj);
        }
    }
}