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

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.type.TypeReference;
import lombok.Value;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.util.ydb.AbstractYDao;
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 avhaliullin
 */
@Repository
public class ChecklistPageSamplesYDao extends AbstractYDao {
    public ChecklistPageSamplesYDao() {
        super(PREFIX_CHECKLIST, "checklist_page_samples");
    }

    public void deleteSamples(List<WebmasterHostId> hostIds, ChecklistSamplesType type) {
        batchDelete(DELETE_BY_TYPE, hostIds.stream().map(e -> Pair.of(e, type)).collect(Collectors.toList())).execute();
    }

    public void deleteSamples(WebmasterHostId hostId, ChecklistSamplesType type) {
        delete()
                .where(F.HOST_ID.eq(hostId))
                .and(F.TYPE.eq(type))
                .execute();
    }

    public void deleteSamples(WebmasterHostId hostId) {
        delete()
                .where(F.HOST_ID.eq(hostId))
                .execute();
    }

    public void deleteSamples(Collection<WebmasterHostId> hostIds) {
        try {
            batchDelete(DELETE_VALUE_MAPPER, hostIds).execute();
        } catch (Exception exp) {
            for (WebmasterHostId hostId : hostIds) {
                delete().where(F.HOST_ID.eq(hostId)).execute();
            }
        }
    }

    public void insertSamplesWithTTL(WebmasterHostId hostId, ChecklistSamplesType type, List<String> samples) {
        upsert(F.HOST_ID.value(hostId), F.TYPE.value(type), F.SAMPLES.value(samples), F.EXPIRED_TIME.value(DateTime.now()))
                .execute();
    }

    public void insertSamples(WebmasterHostId hostId, ChecklistSamplesType type, List<String> samples) {
        upsert(F.HOST_ID.value(hostId), F.TYPE.value(type), F.SAMPLES.value(samples))
                .execute();
    }

    public void insertSamples(List<Pair<WebmasterHostId, List<String>>> list, ChecklistSamplesType type) {
        final List<Row> collect = list.stream().map(e -> new Row(e.getKey(), type, e.getValue())).collect(Collectors.toList());
        batchUpdate(INSERT_MAPPER, collect).execute();
    }

    public List<String> getSamples(WebmasterHostId hostId, ChecklistSamplesType type) {

        List<String> result = select(F.SAMPLES)
                .where(F.HOST_ID.eq(hostId))
                .and(F.TYPE.eq(type))
                .queryOne();
        if (result == null) {
            return Collections.emptyList();
        }
        return result;
    }

    public Set<ChecklistSamplesType> getExistedSamplesTypes(WebmasterHostId hostId) {
        return select(F.TYPE)
                .where(F.HOST_ID.eq(hostId))
                .queryForList().stream().filter(Objects::nonNull).collect(Collectors.toSet());
    }


    private static final ValueDataMapper<WebmasterHostId> DELETE_VALUE_MAPPER = ValueDataMapper.create(
            Pair.of(F.HOST_ID, r -> F.HOST_ID.get(r)));
    private static final ValueDataMapper<Pair<WebmasterHostId, ChecklistSamplesType>> DELETE_BY_TYPE = ValueDataMapper.create(
            Pair.of(F.HOST_ID, r -> F.HOST_ID.get(r.getKey())),
            Pair.of(F.TYPE, r -> F.TYPE.get(r.getValue())));
    private static final ValueDataMapper<Row> INSERT_MAPPER = ValueDataMapper.create(
            Pair.of(F.HOST_ID, r -> F.HOST_ID.get(r.getHostId())),
            Pair.of(F.TYPE, r -> F.TYPE.get(r.getType())),
            Pair.of(F.SAMPLES, r -> F.SAMPLES.get(r.getSamples()))
    );

    @Value
    public static class Row {
        WebmasterHostId hostId;
        ChecklistSamplesType type;
        List<String> samples;
    }

    private static class F {
        public static final TypeReference<List<String>> TYPE_REFERENCE = new TypeReference<>() {
        };
        static final Field<WebmasterHostId> HOST_ID = Fields.hostIdField("host_id");
        static final Field<ChecklistSamplesType> TYPE = Fields.intEnumField("type", ChecklistSamplesType.R);
        static final Field<List<String>> SAMPLES = Fields.compressedJsonField2("samples", TYPE_REFERENCE);
        static final Field<DateTime> EXPIRED_TIME = Fields.jodaDateTimeField("expired_time");
    }
}
