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

import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.Getter;
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.util.enums.IntEnum;
import ru.yandex.webmaster3.core.util.enums.IntEnumResolver;
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 ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.ValueDataMapper;

@Repository
public class NotMainMirrorNotificationsYDao extends AbstractYDao {
    private static final String DB_NAME = PREFIX_NOTIFICATION;
    private static final String TABLE_NAME = "not_main_mirror_notifications";


    protected NotMainMirrorNotificationsYDao() {
        super(DB_NAME, TABLE_NAME);
    }

    public void insert(Collection<NotificationInfo> items) {
        batchInsert(VALUE_MAPPER, items).execute();
    }

    public void insert(NotificationInfo item) {
        upsert(VALUE_MAPPER, item).execute();
    }

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

    public Map<Pair<Long, WebmasterHostId>, NotificationInfo> list(Collection<Long> userIds) {
        return select(MAPPER)
                .where(F.USER_ID.in(userIds))
                .queryForList(
                        Pair.of(F.USER_ID, NotificationInfo::getUserId),
                        Pair.of(F.HOST_ID, NotificationInfo::getMainMirrorHostId)
                ).stream()
                .collect(Collectors.toMap(x -> Pair.of(x.getUserId(), x.getMainMirrorHostId()), x -> x));
    }

    public NotificationInfo get(long userId, WebmasterHostId hostId) {
        return select(MAPPER)
                .where(F.USER_ID.eq(userId))
                .and(F.HOST_ID.eq(hostId))
                .queryOne();
    }

    public void forEach(Consumer<NotificationInfo> consumer) {
        streamReader(MAPPER, consumer);
    }


    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<Type> TYPE = Fields.intEnumField("type", Type.R);
        static final Field<DateTime> SEND_DATE = Fields.jodaDateTimeField("send_date");

    }


    private static final DataMapper<NotificationInfo> MAPPER = DataMapper.create(F.USER_ID, F.HOST_ID, F.TYPE,
            F.SEND_DATE, NotificationInfo::new);

    private static final ValueDataMapper<NotificationInfo> VALUE_MAPPER = ValueDataMapper.create2(
            Pair.of(F.USER_ID, NotificationInfo::getUserId),
            Pair.of(F.HOST_ID, NotificationInfo::getMainMirrorHostId),
            Pair.of(F.TYPE, NotificationInfo::getType),
            Pair.of(F.SEND_DATE, NotificationInfo::getSendDate)
    );

    @Value
    public static class NotificationInfo {
        Long userId;
        WebmasterHostId mainMirrorHostId;
        Type type;
        DateTime sendDate;
    }

    @AllArgsConstructor
    @Getter
    public enum Type implements IntEnum {
        WEEK(0),
        MONTH(1),
        THREE_MONTH(2),
        ;
        int value;

        public static final IntEnumResolver<Type> R = IntEnumResolver.r(Type.class);

        @Override
        public int value() {
            return value;
        }
    }
}
