package ru.yandex.webmaster3.worker.host.moderation;

import com.datastax.driver.core.utils.UUIDs;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.mutable.MutableObject;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
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.host.AllVerifiedHostsCacheService;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.DisplayNameRequest;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.HostDisplayNameModerationRequestState;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.HostDisplayNameModerationYtRequest;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.dao.HostDisplayNameModerationRequestsYDao;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.dao.HostDisplayNameModerationYtRequestsYDao;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.service.DisplayNameService2;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static ru.yandex.webmaster3.storage.host.moderation.camelcase.HostDisplayNameModerationRequestState.IN_PROGRESS;

/**
 * @author leonidrom
 * <p>
 * Заново посылает на модерацию ассессорами заявки, которые по каким то причинам
 * так и не были ими обработаны.
 * В идеале, таких заявок быть не должно, но пока все причины продалбывания заявок
 * не устранены, используется такой вот костыль.
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class HostDisplayNameModerationResendLostRequestsTask extends PeriodicTask<HostDisplayNameModerationResendLostRequestsTask.TaskState> {
    private static DateTime SKIP_REQUESTS_BEFORE_DATE = DateTime.parse("2018-01-25T19:54:52.128+03:00");
    private static int MAX_REQUEST_AGE_IN_DAYS = 14;

    private final HostDisplayNameModerationRequestsYDao hdnModerationRequestsYDao;
    private final HostDisplayNameModerationYtRequestsYDao hdnYtRequestsYDao;
    private final DisplayNameService2 displayNameService2;
    private final AllVerifiedHostsCacheService allVerifiedHostsCacheService;

    @Override
    public Result run(UUID runId) throws Exception {
        TaskState ts = new TaskState();
        setState(ts);

        MutableObject<DisplayNameRequest> maxReqObj = new MutableObject<>(null);
        MutableObject<WebmasterHostId> curHostIdObj = new MutableObject<>(null);
        hdnModerationRequestsYDao.forEach(req -> {
            try {
                var hostId = req.getHostId();
                if (!allVerifiedHostsCacheService.contains(hostId)) {
                    return;
                }

                var curHostId = curHostIdObj.getValue();
                var maxReq = maxReqObj.getValue();
                if (!hostId.equals(curHostId)) {
                    if (maxReq != null) {
                        processReq(maxReq);
                    }

                    maxReqObj.setValue(req);
                    curHostIdObj.setValue(hostId);
                } else {
                    if (req.getCreationDate().isAfter(maxReq.getCreationDate())) {
                        maxReqObj.setValue(req);
                    }
                }
            } catch (WebmasterYdbException e) {
                throw new WebmasterException("Ydb error",
                        new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);
            }
        });

        var maxReq = maxReqObj.getValue();
        if (maxReq != null) {
            processReq(maxReq);
        }

        return new Result(TaskResult.SUCCESS);
    }

    private void processReq(DisplayNameRequest req) {
        boolean isDone = req.getState() != IN_PROGRESS;
        DateTime creationDate = req.getCreationDate();
        long reqAgeInDays = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - creationDate.getMillis());
        if (isDone || reqAgeInDays < MAX_REQUEST_AGE_IN_DAYS || SKIP_REQUESTS_BEFORE_DATE.isAfter(creationDate)) {
            return;
        }

        HostDisplayNameModerationRequestState state;
        var ts = getState();
        if (reqAgeInDays >= 180) {
            state = HostDisplayNameModerationRequestState.CANCELLED;
            ts.totalCanceled++;
        } else {
            state = HostDisplayNameModerationRequestState.IN_PROGRESS;
            ts.totalResend++;
        }

        // создадим новый запрос со свежими датами
        UUID requestId = UUIDs.timeBased();
        WebmasterHostId hostId = req.getHostId();
        DisplayNameRequest newReq = DisplayNameRequest.builder()
                .hostId(hostId)
                .requestId(requestId)
                .displayName(req.getDisplayName())
                .state(state)
                .creationDate(DateTime.now())
                .modificationDate(DateTime.now())
                .isUserClosedInfoBanner(req.isUserClosedInfoBanner())
                .userId(req.getUserId())
                .assessorId(req.getAssessorId())
                .build();

        // сохраним его в базе
        hdnModerationRequestsYDao.saveDisplayNameRequest(newReq);

        // и отправим в Yt
        String currentDisplayName = displayNameService2.getDisplayName(hostId);
        HostDisplayNameModerationYtRequest entry = new HostDisplayNameModerationYtRequest(
                hostId,
                requestId,
                DateTime.now(),
                newReq.getDisplayName(),
                currentDisplayName,
                null,
                false);

        hdnYtRequestsYDao.storeRequest(entry);
    }


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

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

    public class TaskState implements PeriodicTaskState {
        int totalCanceled;
        int totalResend;

        public int getTotalCanceled() {
            return totalCanceled;
        }

        public int getTotalResend() {
            return totalResend;
        }
    }
}
