package io.delphi.qa.auto.awsmfrw.managers.helpers.db;

import com.google.gson.reflect.TypeToken;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaCommonMethods;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaLogging;
import io.delphi.qa.auto.awsmfrw.models.db.BsSqlTable;
import io.qameta.allure.Step;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.stream.Collectors;

public abstract class BsDbSql implements IsaLogging {

  protected abstract Connection getConnection();

  protected <T extends BsSqlTable<T>> HashSet<?>[] buildEntity(List<T> set) {
    HashSet<?>[] result = new HashSet<?>[2];
    HashSet<Object> values = new HashSet<>();
    Map<String, String> mapEntityColumnNames =
        IsaCommonMethods.gson.fromJson(
            IsaCommonMethods.gson.toJson(set.iterator().next()),
            new TypeToken<Map<String, String>>() {}.getType());
    result[0] =
        new HashSet<Object>() {
          {
            add(
                mapEntityColumnNames.entrySet().stream()
                    .map(Map.Entry::getKey)
                    .collect(Collectors.joining(", ")));
          }
        };
    set.forEach(
        model -> {
          Map<String, String> mapEntityColumnValues =
              IsaCommonMethods.gson.fromJson(
                  IsaCommonMethods.gson.toJson(model),
                  new TypeToken<Map<String, String>>() {}.getType());
          values.add(String.format("'%s'", String.join("', '", mapEntityColumnValues.values())));
        });
    result[1] = values;
    return result;
  }

  @Step("Query rows")
  public List<Map<String, String>> queryRows(String queryText) {
    List<Map<String, String>> resultObjectSet = new ArrayList<>();
    IsaLogging.log(this.getClass(), "\n- query text:\n" + queryText);
    try (Connection conn = this.getConnection();
        Statement st = conn.createStatement();
        ResultSet rs = Objects.requireNonNull(st).executeQuery(queryText)) {
      IsaLogging.log(this.getClass(), "\n- connection:\n" + conn.getClientInfo());
      resultObjectSet = resultSet(rs);
    } catch (Throwable e) {
      IsaLogging.log(this.getClass(), "\n- exception:\n" + e);
    } finally {
      IsaLogging.log(this.getClass(), String.format("\n- records: %s", resultObjectSet.size()));
    }
    return resultObjectSet;
  }

  private List<Map<String, String>> resultSet(ResultSet rs) throws SQLException {
    List<Map<String, String>> resultObjectSet = new ArrayList<>();
    while (true) {
      if (rs.next()) {
        Map<String, String> map = new HashMap<>();
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
          String value = rs.getObject(i) != null ? rs.getObject(i).toString() : null;
          map.put(rs.getMetaData().getColumnName(i), value);
        }
        resultObjectSet.add(map);
      } else break;
    }
    return resultObjectSet;
  }
}
