package ru.yandex.webmaster3.worker.checklist;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskState;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.core.worker.task.TaskResult;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistPageSamplesService;
import ru.yandex.webmaster3.storage.checklist.dao.ChecklistSamplesType;
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.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * @author avhaliullin
 */
@Slf4j
@Component("siteProblemsCleanTask")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SiteProblemsCleanTask extends PeriodicTask<SiteProblemsCleanTask.State> {
    private static final int BATCH_SIZE = 2000;

    private final ChecklistPageSamplesService checklistPageSamplesService;
    private final CleanableProblemsYDao cleanableProblemsYDao;
    private final CleanableProblemsLastUpdateYDao cleanableProblemsLastUpdateYDao;
    private final RealTimeSiteProblemsYDao realTimeSiteProblemsYDao;

    @Override
    public PeriodicTaskType getType() {
        return PeriodicTaskType.SITE_PROBLEMS_CLEAN;
    }

    @Override
    public Result run(UUID runId) throws Exception {
        setState(new State());
        Map<SiteProblemTypeEnum, DateTime> lastUpdates = cleanableProblemsLastUpdateYDao.getLastUpdateInfos();
        List<Pair<WebmasterHostId, SiteProblemTypeEnum>> problemsForRemoval = new ArrayList<>(BATCH_SIZE);
        Map<ChecklistSamplesType, Map<WebmasterHostId, List<String>>> sampleMap = new EnumMap<>(ChecklistSamplesType.class);
        cleanableProblemsYDao.listProblems(problem -> {
            var problemType = problem.getProblemType();
            var storageType = problemType.getStorageType();
            if (!storageType.isRealTimeProblem()) {
                return;
            }

            DateTime lastProblemTypeUpdate = lastUpdates.get(problemType);
            WebmasterHostId hostId = problem.getHostId();
            if (lastProblemTypeUpdate != null && lastProblemTypeUpdate.isAfter(problem.getLastUpdate())) {
                problemsForRemoval.add(Pair.of(hostId, problemType));
                final ChecklistSamplesType samplesType = ChecklistSamplesType.byProblemType(problemType);
                if (samplesType != null) {
                    sampleMap.computeIfAbsent(samplesType, k -> new HashMap<>()).put(hostId, List.of());
                }
                if (problemsForRemoval.size() > BATCH_SIZE) {
                    save(problemsForRemoval, sampleMap);
                }
                state.problemsCleaned.compute(problemType, (k, v) -> v == null ? 1 : v + 1);
            }
        });
        save(problemsForRemoval, sampleMap);

        return new Result(TaskResult.SUCCESS);
    }

    private void save(List<Pair<WebmasterHostId, SiteProblemTypeEnum>> problemsForRemoval,
                      Map<ChecklistSamplesType, Map<WebmasterHostId, List<String>>> sampleMap) {
        realTimeSiteProblemsYDao.deleteProblems(problemsForRemoval);
        cleanableProblemsYDao.deleteProblems(problemsForRemoval);
        checklistPageSamplesService.saveSamples(sampleMap);
        problemsForRemoval.clear();
        sampleMap.clear();
    }

    @Override
    public TaskSchedule getSchedule() {
        return TaskSchedule.startByCron("0 20 * * * *");
    }

    public static final class State implements PeriodicTaskState {

        private EnumMap<SiteProblemTypeEnum, Integer> problemsCleaned = new EnumMap<>(SiteProblemTypeEnum.class);

        public EnumMap<SiteProblemTypeEnum, Integer> getProblemsCleaned() {
            return problemsCleaned;
        }
    }
}
