package ru.yandex.webmaster3.coordinator.http.checklist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.coordinator.http.SimpleRequest;
import ru.yandex.webmaster3.coordinator.http.SimpleResponse;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.WriteAction;
import ru.yandex.webmaster3.storage.checklist.dao.CleanableProblemsLastUpdateYDao;
import ru.yandex.webmaster3.storage.checklist.dao.CleanableProblemsYDao;
import ru.yandex.webmaster3.storage.checklist.dao.RealTimeSiteProblemsYDao;
import ru.yandex.webmaster3.storage.checklist.data.CleanableProblem;
import ru.yandex.webmaster3.storage.checklist.service.SiteProblemStorageService;

/**
 * Created by Oleg Bazdyrev on 05/07/2021.
 */
@Slf4j
@WriteAction
@Component("/checklist/fixCleanableProblems")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class FixCleanableProblemsAction extends Action<SimpleRequest, SimpleResponse> {

    private final CleanableProblemsYDao cleanableProblemsYDao;
    private final CleanableProblemsLastUpdateYDao cleanableProblemsLastUpdateYDao;
    private final RealTimeSiteProblemsYDao realTimeSiteProblemsYDao;
    private final SiteProblemStorageService siteProblemStorageService;

    @Override
    public SimpleResponse process(SimpleRequest request) throws WebmasterException {
        Map<SiteProblemTypeEnum, DateTime> lastUpdateInfos = cleanableProblemsLastUpdateYDao.getLastUpdateInfos();
        Map<WebmasterHostId, Set<SiteProblemTypeEnum>> cleanableProblems = new HashMap<>();
        cleanableProblemsYDao.listProblems(cleanableProblem -> {
            WebmasterHostId hostId = cleanableProblem.getHostId();
            SiteProblemTypeEnum problemType = cleanableProblem.getProblemType();
            if (!siteProblemStorageService.toProblemHostId(hostId, problemType).equals(hostId)) {
                log.info("Clearing bad cleanable problem {} for host {}", problemType.name(), hostId);
                cleanableProblemsYDao.deleteProblem(hostId, problemType);
            }

            cleanableProblems.computeIfAbsent(hostId, (k) -> EnumSet.noneOf(SiteProblemTypeEnum.class)).add(problemType);
        });
        AtomicLong goodProblems = new AtomicLong();
        AtomicLong badProblems = new AtomicLong();
        List<CleanableProblem> batch = new ArrayList<>();
        realTimeSiteProblemsYDao.forEach(info -> {
            if (info == null || info.getProblemType() == null || info.getHostId() == null || !info.getState().isPresent()) {
                return;
            }
            if (info.getProblemType().isDisabled()) {
                return;
            }
            DateTime lastUpdateInfo = lastUpdateInfos.get(info.getProblemType());
            if (lastUpdateInfo == null) {
                return;
            }
            if (cleanableProblems.getOrDefault(info.getHostId(), Collections.emptySet()).contains(info.getProblemType())) {
                goodProblems.incrementAndGet();
                return;
            }
            if (!siteProblemStorageService.toProblemHostId(info.getHostId(), info.getProblemType()).equals(info.getHostId())) {
                return;
            }
            log.info("Host {} does not have cleanable problem {}", info.getHostId(), info.getProblemType().name());
            batch.add(new CleanableProblem(info.getProblemType(), lastUpdateInfo, info.getHostId()));
            badProblems.incrementAndGet();
            if (batch.size() >= 100) {
                cleanableProblemsYDao.updateProblems(batch);
                batch.clear();
            }
        });
        if (!batch.isEmpty()) {
            cleanableProblemsYDao.updateProblems(batch);
        }
        log.info("Total good problems: {}", goodProblems.get());
        log.info("Total bad problems: {}", badProblems.get());

        return new SimpleResponse();
    }
}
