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

import com.google.common.base.Preconditions;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
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.DataMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Field;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Fields;

import java.util.*;

/**
 * @author leonidrom
 */
@Repository
public class FrontendUserHostSettingsYDao extends AbstractYDao {
    public FrontendUserHostSettingsYDao() {
        super(PREFIX_USER, "frontend_user_host_settings");
    }

    public void updateValue(long userId, WebmasterHostId hostId, FrontendUserHostSettingsKey key, String value) {
        upsert(
                F.USER_ID.value(userId),
                F.HOST_ID.value(hostId),
                F.KEY.value(key),
                F.LAST_UPDATE.value(DateTime.now()),
                F.VALUE.value(value)
        ).execute();
    }

    public void deleteKey(long userId, WebmasterHostId hostId, FrontendUserHostSettingsKey key) {
        delete()
                .where(F.USER_ID.eq(userId))
                .and(F.HOST_ID.eq(hostId))
                .and(F.KEY.eq(key))
                .execute();
    }

    public void deleteForUser(long userId) {
        delete().where(F.USER_ID.eq(userId)).execute();
    }

    public String getValue(long userId, WebmasterHostId hostId, FrontendUserHostSettingsKey key)  {
        return select(F.VALUE)
                .where(F.USER_ID.eq(userId))
                .and(F.HOST_ID.eq(hostId))
                .and(F.KEY.eq(key))
                .queryOne();
    }

    public Map<WebmasterHostId, Map<FrontendUserHostSettingsKey, String>> getValues(long userId) {
        List<Triple<FrontendUserHostSettingsKey, String, WebmasterHostId>> userSettingsList;
        userSettingsList = select(DataMapper.create(F.KEY, F.VALUE, F.HOST_ID, Triple::of))
                .where(F.USER_ID.eq(userId))
                .queryForList(
                        Pair.of(F.HOST_ID, Triple::getRight),
                        Pair.of(F.KEY, Triple::getLeft)
                );

        Map<WebmasterHostId, Map<FrontendUserHostSettingsKey, String>> result = new TreeMap<>();
        userSettingsList.forEach(triple -> {
            FrontendUserHostSettingsKey key = triple.getLeft();
            if (key == null) {
                return;
            }
            String value = triple.getMiddle();
            WebmasterHostId hostId = triple.getRight();
            result.computeIfAbsent(hostId, k -> new EnumMap<>(FrontendUserHostSettingsKey.class)).put(key, value);
        });

        return result;
    }

    public Map<FrontendUserHostSettingsKey, String> getValues(long userId, WebmasterHostId hostId, Collection<FrontendUserHostSettingsKey> keys) {
        Preconditions.checkArgument(keys.size() <= YDB_SELECT_ROWS_LIMIT,
                "Collection size exceeds YDB limitation");

        var lst = select(MAPPER)
                .where(F.USER_ID.eq(userId))
                .and(F.HOST_ID.eq(hostId))
                .and(F.KEY.in(keys))
                .queryForList();

        Map<FrontendUserHostSettingsKey, String> result = new HashMap<>();
        lst.forEach(q -> result.put(q.getLeft(), q.getRight()));
        return result;
    }

    private static class F {
        static final Field<Long> USER_ID = Fields.longField("user_id");
        static final Field<WebmasterHostId> HOST_ID = Fields.hostIdField("host_id");
        static final Field<FrontendUserHostSettingsKey> KEY = Fields.stringEnumField("key", FrontendUserHostSettingsKey.R);
        static final Field<DateTime> LAST_UPDATE = Fields.jodaDateTimeField("last_update");
        static final Field<String> VALUE = Fields.stringField("value").makeOptional();
    }

    public static final DataMapper<Pair<FrontendUserHostSettingsKey, String>> MAPPER = DataMapper.create(F.KEY, F.VALUE, Pair::of);
}
