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

import com.google.gson.reflect.TypeToken;
import io.delphi.qa.auto.awsmfrw.common_baby.ICanProvideHelpers;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaCommonMethods;
import io.delphi.qa.auto.awsmfrw.models.db.BsSqlTable;
import io.delphi.qa.auto.awsmfrw.models.db.sql.pg.BsPgEntity;
import io.delphi.qa.auto.awsmfrw.suppliers.DelphiDataContainer;
import io.qameta.allure.Step;

import java.util.*;
import java.util.stream.Collectors;

public class HlPostgres extends BsPostgres implements IsaCommonMethods, ICanProvideHelpers {

  private String STR_UPDATE_QUERY_PATTERN =
      "update\n\t{schema}.{table}\nset\n\t{set}\nwhere\n\t{where}\nreturning *;";
  private String STR_DELETE_QUERY_PATTERN = "DELETE FROM\n\t{schema}.{table}\nWHERE\n\t{where};";
  private String STR_SELECT_QUERY_PATTERN =
      "select\n\t*\nfrom\n\t{schema}.{table}\nwhere\n\t{where};";
  private String STR_INSERT_QUERY_PATTERN =
      "insert into\n\t{schema}.{table}\n\t({columns})\n\t\tVALUES\n\t({values})\nON CONFLICT DO NOTHING\nRETURNING *;";

    public HlPostgres(Map<String, String> secretMap) {
        super(secretMap);
    }

    @Step("DB::Postgres: do delete")
  public <T extends BsPgEntity<T>> int awsmDelete(Set<T> set, final String column) {
    if (set.size() > 0) {
      String where =
          set.stream().map(T::getId).map(String::valueOf).collect(Collectors.joining(", "));
      return super.executeUpdate(
          IsaCommonMethods.resolvePattern(
              STR_DELETE_QUERY_PATTERN,
              new HashMap<String, String>() {
                {
                  put("schema", "public");
                  String tableName = set.iterator().next().tableName();
                  put("table", tableName);
                    if (column != null) {
                        put("where", String.format("%s in (%s)", column, where));
                    } else {
                        put("where", String.format("%s in (%s)", tableName + "_id", where));
                    }
                }
              }));
    } else return 0;
  }

  @Step("DB::Postgres: do insert")
  public <T extends BsPgEntity<T>> Set<T> awsmInsert(Set<T> set) {
    HashSet<?>[] entity = buildEntity(set);
    return super.executeQuery(
        IsaCommonMethods.resolvePattern(
            STR_INSERT_QUERY_PATTERN,
            new HashMap<String, String>() {
              {
                put("schema", "public");
                put("table", set.iterator().next().tableName());
                put("columns", entity[0].iterator().next().toString());
                String collect =
                    entity[1].stream().map(Object::toString).collect(Collectors.joining("),\n("));
                put("values", collect);
              }
            }),
        set.iterator().next());
  }

  @Deprecated
  @Step("execute sql query")
  public List<String> awsmQuery(String query) {
    return super.executeCustomQuery(query);
  }


  @Step("execute sql query")
  public List<Map<String, String>> awsmQueryNew(String query) {
    return super.queryRows(query);
  }

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

  @Step("DB::Postgres: do select")
  public <T extends BsSqlTable<T>> Set<T> awsmSelect(T obj, String where) {
    return super.executeQuery(
        IsaCommonMethods.resolvePattern(
            STR_SELECT_QUERY_PATTERN,
            new HashMap<String, String>() {
              {
                put("schema", "public");
                put("table", obj.tableName());
                if (where != null) {
                  put("where", where);
                } else {
                  put("where", obj.tableName() + "_id != 0");
                }
              }
            }),
        obj);
  }

  @Step("DB::Postgres: do update")
  public <T extends BsPgEntity<T>> Set<T> awsmUpdate(T obj) {
    final Long id = obj.getId();
    obj.withId(null);
    Map<String, String> queryParamsMap =
        gson.fromJson(gson.toJson(obj), new TypeToken<Map<String, String>>() {}.getType());
    return super.executeQuery(
        IsaCommonMethods.resolvePattern(
            STR_UPDATE_QUERY_PATTERN,
            new HashMap<String, String>() {
              {
                put("schema", "public");
                put("table", obj.tableName());
                put(
                    "set",
                    queryParamsMap.entrySet().stream()
                        .map(o -> String.format("%s = '%s'", o.getKey(), o.getValue()))
                        .collect(Collectors.joining("\n, ")));
                put("where", String.format("unit_of_work_id = %s", Objects.requireNonNull(id)));
              }
            }),
        obj);
  }

    @Override
    public HlPostgres init() {
        return this;
    }

    @Override
    public void query(String context) {
        List<String> strings = this.awsmQuery(context);
        DelphiDataContainer.saveResource("POSTGRES_RESULT_LOCATOR", strings);
    }
}
