package ru.yandex.webmaster3.storage.settings.dao;

import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;

import ru.yandex.webmaster3.core.util.enums.EnumResolver;
import ru.yandex.webmaster3.storage.settings.data.AbstractCommonDataState;
import ru.yandex.webmaster3.storage.util.ydb.AbstractYDao;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.Insert;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.DataMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Field;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Fields;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.ValueDataMapper;

/*
 * @author kravchenko99
 * @date 8/21/20
 */
public abstract class AbstractCommonDataStateYDao<T extends Enum<T>, DS extends AbstractCommonDataState<T>> extends AbstractYDao {
    private final F<T> F;


    public AbstractCommonDataStateYDao(String tablePrefix, String tableName, EnumResolver<T> enumResolver) {
        super(tablePrefix, tableName);
        this.F = new F<>(enumResolver);
    }

    public void update(DS commonDataState) {
        var st = upsert(F.DATA_TYPE.value(commonDataState.getType()),
                F.LAST_UPDATE.value(commonDataState.getLastUpdate()),
                F.VALUE.value(commonDataState.getValue()));
        execute(st);
    }

    @Nullable
    public DS getValue(T type) {
        return queryOne(select(getMapper()).where(F.DATA_TYPE.eq(type)).getStatement(), getMapper());
    }

    public List<DS> listValues() {
        return queryForList(select(getMapper()), getMapper()).stream()
                .filter(x -> x.getType() != null)
                .collect(Collectors.toList());
    }

    public void batchInsert(List<DS> items) {
        execute(batchInsert(getValueMapper(), items));
    }

    protected abstract DS createData(T type, String value, DateTime updateDate);

    protected DataMapper<DS> getMapper() {
        return DataMapper.create(
                F.DATA_TYPE, F.VALUE, F.LAST_UPDATE, this::createData
        );
    }

    protected ValueDataMapper<DS> getValueMapper() {
        return ValueDataMapper.create(
                Pair.of(F.DATA_TYPE, e -> F.DATA_TYPE.get(e.getType())),
                Pair.of(F.LAST_UPDATE, e -> F.LAST_UPDATE.get(e.getLastUpdate())),
                Pair.of(F.VALUE, e -> F.VALUE.get(e.getValue()))
        );
    }

    private static class F<T extends Enum<T>> {
        final Field<String> VALUE = Fields.stringField("value");
        final Field<DateTime> LAST_UPDATE = Fields.jodaDateTimeField("last_update");
        final Field<T> DATA_TYPE;

        public F(EnumResolver<T> enumResolver) {
            this.DATA_TYPE = Fields.stringEnumField("data_type", enumResolver);
        }
    }
}
