package de.ivu.elect.business.system.control;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;
import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.system.boundary.SystemLogWriter;
import de.ivu.elect.business.system.entity.SystemLogQuelle;

public class SqlRunner implements Serializable {

  public static final long MAX_ROWS = 5_000;

  private static final String NULL_VALUE = "(null)";

  private static final AtomicBoolean UPDATE_ENABLED = new AtomicBoolean(false);

  private static final long serialVersionUID = 1L;

  @Inject
  private transient DatasourceJndiResolver datasourceJndiResolver;

  @Inject
  private transient SystemLogWriter systemLogWriter;

  public String query(String sql) throws SQLException {

    List<List<String>> columns = new ArrayList<>();

    DataSource datasource = datasourceJndiResolver.getDatasource();
    int row = 1;
    try (Connection con = datasource.getConnection()) {
      try (Statement stmt = con.createStatement()) {
        try (ResultSet rs = stmt.executeQuery(sql)) {
          ResultSetMetaData metaData = rs.getMetaData();
          for (int i = 0; i < metaData.getColumnCount(); i++) {
            String columnLabel = metaData.getColumnLabel(i+1);
            ArrayList<String> column = new ArrayList<>();
            columns.add(column);
            column.add(columnLabel);
          }
          while (rs.next()) {
            if (row > MAX_ROWS) {
              break;
            }

            row++;

            for (int i = 0; i < metaData.getColumnCount(); i++) {
              Object object = rs.getObject(i+1);
              if (rs.wasNull() || object == null) {
                object = NULL_VALUE;
              }
              columns.get(i).add(object.toString());
            }
          }
        }
      }
    }

    List<Long> columnWidth = bestimmeBreiten(columns);

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < row; i++) {
      sb.append("\n");
      String separator = "|";
      long extraWidth = 1;
      if (i == 0) {
        separator = "||";
        extraWidth = 0;
      }
      sb.append(separator);
      for (int colIndex = 0; colIndex < columns.size(); colIndex++) {
        long width = columnWidth.get(colIndex) + extraWidth;
        if (colIndex == 0 && extraWidth > 0) {
          width++;
        }
        add(columns.get(colIndex).get(i), sb, separator, width);
      }
    }


    return sb.toString();
  }

  public String update(String sql) throws SQLException {
    if (UPDATE_ENABLED.get()) {
      systemLogWriter.writeWarnLogMessageFuerSystemMandant("SQL-Update: " + sql, SystemLogQuelle.SYSTEM);
      DataSource datasource = datasourceJndiResolver.getDatasource();
      try (Connection con = datasource.getConnection()) {
        try (Statement stmt = con.createStatement()) {
          return Integer.toString(stmt.executeUpdate(sql));
        }
      } finally {
        UPDATE_ENABLED.set(false);
      }

    }
    return "sql update disabled";
  }

  public void enabledUpdate() {
    UPDATE_ENABLED.set(true);
  }

  private List<Long> bestimmeBreiten(List<List<String>> columns) {
    List<Long> result = new ArrayList<>();
    for (List<String> column : columns) {
      result.add(bestimmeMaximaleBreite(column));
    }

    return result;
  }

  private Long bestimmeMaximaleBreite(List<String> column) {
    return (long) column.stream().mapToInt(String::length).max().orElse(1);
  }

  private void add(String string, StringBuilder sb, String separator, Long width) {
    sb.append(StringUtils.leftPad(string, width.intValue(), " "));
    sb.append(separator);
  }

}
