package databaseconnector.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import com.mendix.logging.ILogNode;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.systemwideinterfaces.core.IMendixObject;

import databaseconnector.interfaces.ConnectionManager;
import databaseconnector.interfaces.ObjectInstantiator;

public class JdbcConnector {
  private final ILogNode logNode;
  private ObjectInstantiator objectInstantiator;
  private ConnectionManager connectionManager;

  public JdbcConnector(final ILogNode logNode, ObjectInstantiator objectInstantiator, ConnectionManager connectionManager) {
    this.logNode = logNode;
    this.objectInstantiator = objectInstantiator;
    this.connectionManager = connectionManager;
  }

  public JdbcConnector(ILogNode logNode) {
    this(logNode, new ObjectInstantiatorImpl(), ConnectionManagerSingleton.getInstance());
  }

  public List<IMendixObject> executeQuery(String jdbcUrl, String userName, String password, String entityName, String sql,
      IContext context) throws SQLException {
    List<IMendixObject> resultList = new ArrayList<>();

    executeQuery(jdbcUrl, userName, password, sql, context, row -> {
      IMendixObject obj = objectInstantiator.instantiate(context, entityName);
      row.stream().forEach(c -> obj.setValue(context, c.name, c.value));
      resultList.add(obj);
      logNode.info("obj: " + obj);
    });

    logNode.info(String.format("List: %d", resultList.size()));
    return resultList;
  }

  public String executeQueryToJson(String jdbcUrl, String userName, String password, String sql, IContext context) throws SQLException {
    return null;
  }

  public void executeQuery(String jdbcUrl, String userName, String password, String sql,
      IContext context, Consumer<List<Column>> consumer) throws SQLException {
    logNode.info(String.format("executeQuery: %s, %s, %s", jdbcUrl, userName, sql));

    // TODO: make sure user doesn't crash runtime due to retrieving too many records
    try (Connection connection = connectionManager.getConnection(jdbcUrl, userName, password);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery()) {
      ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
      int columnCount = resultSetMetaData.getColumnCount();

      while (resultSet.next()) {
        ArrayList<Column> row = new ArrayList<Column>();

        for (int i = 0; i < columnCount; i++) {
          String columnName = resultSetMetaData.getColumnName(i + 1);
          Object columnValue = resultSet.getObject(i + 1);
          logNode.info(String.format("setting col: %s = %s", columnName, columnValue));
          row.add(new Column(columnName, columnValue));
        }

        consumer.accept(row);
      }
    }
  }

  public class Column {
    public String name;
    public Object value;

    public Column(String name, Object value) {
      this.name = name;
      this.value = value;
    }
  }

  public long executeStatement(String jdbcUrl, String userName, String password, String sql) throws SQLException {
    logNode.info(String.format("executeStatement: %s, %s, %s", jdbcUrl, userName, sql));

    try (Connection connection = connectionManager.getConnection(jdbcUrl, userName, password);
        PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
      return preparedStatement.executeUpdate();
    }
  }
}

@@ -234,20 +234,70 @@
 va.util.
-List
+HashMap;%0D%0Aimport java.util.List;%0D%0Aimport java.util.Map
 ;%0D%0Aimpor
@@ -1407,67 +1407,58 @@
     
-executeQuery(jdbcUrl, userName, password, sql, context, row
+Consumer%3CMap%3CString, Object%3E%3E rowHandler = columns
  -%3E 
@@ -1550,20 +1550,15 @@
     
-row.stream()
+columns
 .for
@@ -1562,17 +1562,22 @@
 forEach(
-c
+(n, v)
  -%3E obj.
@@ -1598,23 +1598,12 @@
 xt, 
-c.name, c.value
+n, v
 ));%0D
@@ -1672,16 +1672,82 @@
 ;%0D%0A    %7D
+;%0D%0A%0D%0A    executeQuery(jdbcUrl, userName, password, sql, rowHandler
 );%0D%0A%0D%0A  
@@ -2103,46 +2103,35 @@
     
-IContext context, Consumer%3CList%3CColumn
+Consumer%3CMap%3CString, Object
 %3E%3E c
@@ -2759,52 +2759,46 @@
     
-ArrayList%3CColumn%3E row = new ArrayList%3CColumn
+Map%3CString, Object%3E row = new HashMap%3C
 %3E();
@@ -3088,22 +3088,11 @@
 row.
-add(new Column
+put
 (col
@@ -3104,33 +3104,32 @@
 me, columnValue)
-)
 ;%0D%0A        %7D%0D%0A%0D%0A
@@ -3186,200 +3186,8 @@
 %0D%0A%0D%0A
-  public class Column %7B%0D%0A    public String name;%0D%0A    public Object value;%0D%0A%0D%0A    public Column(String name, Object value) %7B%0D%0A      this.name = name;%0D%0A      this.value = value;%0D%0A    %7D%0D%0A  %7D%0D%0A%0D%0A
   pu

Simplify executeQuery implementation.