package ru.micord.ervu.account_applications.dao;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Table;
import org.jooq.UpdatableRecord;
import org.jooq.impl.DSL;

/**
 * @author Adel Kalimullin
 */
public abstract class AbstractDataDao<T extends UpdatableRecord<T>> {
  protected final DSLContext dsl;

  protected AbstractDataDao(DSLContext dsl) {
    this.dsl = dsl;
  }

  public T newRecord() {
    return dsl.newRecord(getTable());
  }

  protected abstract Table<T> getTable();

  public void upsertData(T record) {
    record.merge();
  }

  public <F> Set<F> getExistingValuesByField(Field<F> field, List<F> values) {
    return dsl.select(field)
        .from(getTable())
        .where(field.in(values))
        .fetchSet(field);
  }

  protected void deleteByFieldInValues(Field<String> field, List<String> values) {
    dsl.deleteFrom(getTable())
        .where(field.in(values))
        .execute();
  }

  protected <V> void deleteByValues(Map<Field<V>, V> fieldValues) {
    Condition condition = DSL.trueCondition();

    for (var entry : fieldValues.entrySet()) {
      Field<V> field = entry.getKey();
      V value = entry.getValue();
      condition = condition.and(field.eq(value));
    }

    dsl.deleteFrom(getTable())
        .where(condition)
        .execute();
  }

  protected <E, I> void deleteByFieldAndInValues(Field<E> equalField, E equalValue,
      Field<I> inField, List<I> inValues) {
    dsl.deleteFrom(getTable())
        .where(equalField.eq(equalValue))
        .and(inField.in(inValues))
        .execute();
  }

  public <S, F> Optional<S> getValueByField(Field<S> selectField, Field<F> filterField,
      F filterValue) {
    return dsl.select(selectField)
        .from(getTable())
        .where(filterField.eq(filterValue))
        .fetchOptional(selectField);
  }

  protected <S, F> List<S> getValuesByField(Field<S> selectField, Field<F> filterField,
      F filterValue) {
    return dsl.select(selectField)
        .from(getTable())
        .where(filterField.eq(filterValue))
        .fetch(selectField);
  }

  protected <V, F> void setFieldByField(Field<V> targetField, V value, Field<F> whereField,
      F whereValue) {
    dsl.update(getTable())
        .set(targetField, value)
        .where(whereField.eq(whereValue))
        .execute();
  }

  public void mergeRecords(List<T> records) {
    dsl.batchMerge(records).execute();
  }
}