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

import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemStorageType;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostGeneration;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.checklist.dao.RealTimeSiteProblemsYDao;
import ru.yandex.webmaster3.storage.checklist.data.AbstractProblemInfo;
import ru.yandex.webmaster3.storage.checklist.data.RealTimeSiteProblemInfo;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
public class RealTimeSiteProblemsProvider implements SiteProblemsProvider {

    private Set<SiteProblemTypeEnum> supportedProblemTypes;

    private SiteProblemStorageType providerStorageType;
    private final RealTimeSiteProblemsYDao realTimeSiteProblemsYDao;
    private final SiteProblemStorageService siteProblemStorageService;


    @Override
    public @NotNull List<? extends AbstractProblemInfo> listProblems(@NotNull WebmasterHostId hostId, @Nullable UUID generationId) {
        return filterProblems(realTimeSiteProblemsYDao.listSiteProblems(toProblemHostId(hostId)));
    }

    @Override
    public Map<WebmasterHostId, List<? extends AbstractProblemInfo>> listProblems(List<WebmasterHostGeneration> hosts) {
        Map<WebmasterHostId, WebmasterHostId> hostIdToProblemHostId = hosts.stream()
                .map(WebmasterHostGeneration::getHostId)
                .map(hostId -> Maps.immutableEntry(hostId, toProblemHostId(hostId)))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        Map<WebmasterHostId, List<RealTimeSiteProblemInfo>> problemsMap = realTimeSiteProblemsYDao.listSitesProblems(hostIdToProblemHostId.values()).entrySet().stream()
                .map(e -> Maps.immutableEntry(e.getKey(), filterProblems(e.getValue())))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        return hostIdToProblemHostId.entrySet().stream()
                .map(e -> {
                    var hostId = e.getKey();
                    var problemHostId = e.getValue();
                    return Maps.immutableEntry(hostId, problemsMap.get(problemHostId));
                })
                .filter(e -> e.getValue() != null)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    private WebmasterHostId toProblemHostId(WebmasterHostId hostId) {
        return siteProblemStorageService.toProblemHostId(hostId, providerStorageType);
    }

    @Override
    @Nullable
    public AbstractProblemInfo getProblem(@NotNull WebmasterHostId hostId, @Nullable UUID generationId, @NotNull SiteProblemTypeEnum problemType) {
        hostId = toProblemHostId(hostId);
        return Optional.ofNullable(realTimeSiteProblemsYDao.getProblemInfo(hostId, problemType)).filter(info -> info.getProblemType().getStorageType() == providerStorageType).orElse(null);
    }

    @Override
    public @NotNull Set<SiteProblemTypeEnum> getSupportedProblemTypes() {
        return supportedProblemTypes;
    }

    private List<RealTimeSiteProblemInfo> filterProblems(List<RealTimeSiteProblemInfo> problems) {
        return problems.stream()
                .filter(info -> info.getProblemType().getStorageType() == providerStorageType)
                .collect(Collectors.toList());
    }

    public void setProviderStorageType(SiteProblemStorageType providerStorageType) {
        this.providerStorageType = providerStorageType;
        this.supportedProblemTypes = Collections.unmodifiableSet(Stream.of(SiteProblemTypeEnum.values())
                .filter(type -> type.getStorageType() == providerStorageType)
                .filter(type -> !type.isDisabled())
                .collect(Collectors.toCollection(() -> EnumSet.noneOf(SiteProblemTypeEnum.class))));
    }
}
