package ru.yandex.webmaster3.storage.host.moderation.regions.dao;

import java.util.Comparator;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;

import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.host.moderation.regions.HostRegionsModerationRequest;
import ru.yandex.webmaster3.storage.host.moderation.regions.HostRegionsModerationRequestStatus;
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;

/**
 * @author leonidrom
 */
@Repository
public class HostRegionsModerationRequestsYDao extends AbstractYDao {
    private static final String TABLE_NAME = "host_regions_mod_reqs";

    HostRegionsModerationRequestsYDao() {
        super(PREFIX_REGION, TABLE_NAME);
    }

    public HostRegionsModerationRequest getLastRecord(WebmasterHostId hostId) {
        final Optional<HostRegionsModerationRequest> max = select(MAPPER)
                .where(F.HOST_ID.eq(hostId))
                .queryForList().stream().max(Comparator.comparingLong(a -> UUIDs.unixTimestamp(a.getRequestId())));
        return max.orElse(null);
    }

    public void hideRecord(WebmasterHostId hostId, UUID requestId) {
        update(F.USER_CLOSED.set(true))
                .where(F.HOST_ID.eq(hostId))
                .and(F.REQUEST_ID.eq(requestId))
                .and(F.USER_CLOSED.eq(false))
        .execute();
    }

    public void addRecord(UUID requestId, WebmasterHostId hostId,
                          String assessorId, HostRegionsModerationRequestStatus status,
                          Set<Integer> currentRegions, Set<Integer> requestedRegions, Set<Integer> acceptedRegions,
                          String evidenceUrl) {

        insert(
                F.HOST_ID.value(hostId),
                F.REQUEST_ID.value(requestId),
                F.STATUS.value(status.value()),
                F.CURRENT_REGIONS.value(currentRegions),
                F.REQUESTED_REGIONS.value(requestedRegions),
                F.ACCEPTED_REGIONS.value(acceptedRegions),
                F.ASSESSOR_ID.value(assessorId),
                F.USER_CLOSED.value(false),
                F.EVIDENCE_URL.value(evidenceUrl)
        ).execute();
    }

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

    private static final class F {
        private static final TypeReference<Set<Integer>> INTEGER_SET = new TypeReference<>() {
        };

        private static final Field<WebmasterHostId> HOST_ID = Fields.hostIdField("host_id");
        private static final Field<UUID> REQUEST_ID = Fields.uuidField("request_id");
        private static final Field<Integer> STATUS = Fields.intField("status");
        private static final Field<Set<Integer>> CURRENT_REGIONS = Fields.setField("current_regions", INTEGER_SET);
        private static final Field<Set<Integer>> REQUESTED_REGIONS = Fields.setField("requested_regions", INTEGER_SET);
        private static final Field<Set<Integer>> ACCEPTED_REGIONS = Fields.setField("accepted_regions", INTEGER_SET);
        private static final Field<String> ASSESSOR_ID = Fields.stringField("assessor_id").makeOptional();
        private static final Field<Boolean> USER_CLOSED = Fields.boolField("user_closed").withDefault(false);
        private static final Field<String> EVIDENCE_URL = Fields.stringField("evidence_url").makeOptional();
    }

    private static final DataMapper<HostRegionsModerationRequest> MAPPER = DataMapper.create(
            F.HOST_ID, F.REQUEST_ID, F.STATUS, F.CURRENT_REGIONS, F.REQUESTED_REGIONS, F.ACCEPTED_REGIONS, F.ASSESSOR_ID, F.USER_CLOSED, F.EVIDENCE_URL,
            (hostId, requestId, status, currentRegions, requestedRegions, acceptedRegions, assessorId, userClosed, evidenceUrl) -> new HostRegionsModerationRequest(
                    hostId,
                    requestId,
                    new DateTime(UUIDs.unixTimestamp(requestId)),
                    HostRegionsModerationRequestStatus.R.fromValue(status),
                    currentRegions,
                    requestedRegions,
                    acceptedRegions,
                    assessorId,
                    userClosed,
                    evidenceUrl)
    );
}
