package ru.yandex.webmaster3.worker.mirrors;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

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 ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.checklist.data.SiteProblemContent.MainMirrorIsNotHttps;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.host.service.HostOwnerService;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.util.W3Collectors;
import ru.yandex.webmaster3.core.util.concurrent.graph.BlockingBatchConsumer;
import ru.yandex.webmaster3.core.util.concurrent.graph.GraphExecution;
import ru.yandex.webmaster3.core.util.concurrent.graph.GraphExecutionBuilder;
import ru.yandex.webmaster3.core.util.concurrent.graph.GraphExecutionBuilder.Queue;
import ru.yandex.webmaster3.core.util.concurrent.graph.GraphOutQueue;
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.data.ProblemSignal;
import ru.yandex.webmaster3.storage.checklist.service.SiteProblemsService;
import ru.yandex.webmaster3.storage.host.AllHostsCacheService;
import ru.yandex.webmaster3.storage.host.service.MirrorService2;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

import static ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum.MAIN_MIRROR_IS_NOT_HTTPS;

/**
 * Created by Oleg Bazdyrev on 24/01/2019.
 */
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UpdateMainMirrorIsNotHttpsProblemsTask extends PeriodicTask<UpdateMainMirrorIsNotHttpsProblemsTask.State> {

    private final AllHostsCacheService allHostsCacheService;
    private final HostOwnerService hostOwnerService;
    private final MirrorService2 mirrorService2;
    private final SiteProblemsService siteProblemsService;

    @Override
    public Result run(UUID runId) throws Exception {
        setState(new State());
        GraphExecutionBuilder graphBuilder = new GraphExecutionBuilder("https-problems-updater");

        Queue<Pair<WebmasterHostId, ProblemSignal>> problemsQueue = graphBuilder.process(
                () -> (BlockingBatchConsumer<Pair<WebmasterHostId, ProblemSignal>>) batch -> {
                    try {
                        siteProblemsService.updateCleanableProblems(
                                batch.stream().collect(W3Collectors.toHashMap()), MAIN_MIRROR_IS_NOT_HTTPS);
                    } catch (Exception exp) {
                        log.error(exp.getMessage(), exp);
                        throw exp;
                    }
                }

        ).batchLimit(100).forceFullBatch().concurrency(8).getInput();

        Queue<WebmasterHostId> hostsQueue = graphBuilder.<WebmasterHostId, Pair<WebmasterHostId, ProblemSignal>>process(
                problemsQueue, (GraphOutQueue<Pair<WebmasterHostId, ProblemSignal>> q) -> batch -> {
                    for (WebmasterHostId hostId : batch) {
                        if (mirrorService2.isMainMirror(hostId)) {
                            q.put(Pair.of(hostId, new ProblemSignal(new MainMirrorIsNotHttps(), DateTime.now())));
                            state.problemsCreated.incrementAndGet();
                        }
                    }
                }).concurrency(8).getInput();

        DateTime updateStarted = DateTime.now();
        try (GraphExecution<WebmasterHostId> graph = graphBuilder.build(hostsQueue)) {
            graph.start();
            allHostsCacheService.foreachHost(hostId -> {
                state.totalHosts.incrementAndGet();
                // игнорим HTTPS
                if (hostId.getSchema() == WebmasterHostId.Schema.HTTP && isOwner(hostId)) {
                    try {
                        graph.put(hostId);
                    } catch (InterruptedException e) {
                        throw new WebmasterException("Interrupted!",
                                new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Interrupted!"), e);
                    }
                }
            });
            graph.doneWritingAndAwaitTermination();
        }
        siteProblemsService.notifyCleanableProblemUpdateFinished(MAIN_MIRROR_IS_NOT_HTTPS, updateStarted);

        return new Result(TaskResult.SUCCESS);
    }

    private boolean isOwner(WebmasterHostId hostId) {
        return hostOwnerService.getHostOwner(hostId.getPunycodeHostname()).equalsIgnoreCase(hostId.getPunycodeHostname());
    }

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

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


    public static final class State implements PeriodicTaskState {
        private AtomicInteger totalHosts = new AtomicInteger();
        private AtomicInteger problemsCreated = new AtomicInteger();
        private AtomicInteger problemsCleaned = new AtomicInteger();

        public int getTotalHosts() {
            return totalHosts.get();
        }

        public int getProblemsCreated() {
            return problemsCreated.get();
        }

        public int getProblemsCleaned() {
            return problemsCleaned.get();
        }
    }
}
