package ru.yandex.webmaster3.viewer.http.delurl;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

import com.datastax.driver.core.utils.UUIDs;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.addurl.RecrawlServiceException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.delurl.DelUrlRequest;
import ru.yandex.webmaster3.core.delurl.DelurlState;
import ru.yandex.webmaster3.core.delurl.DelurlType;
import ru.yandex.webmaster3.core.delurl.ProcessDelurlRequestTaskData;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.http.WriteAction;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.util.DailyQuotaUtil;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.worker.client.WorkerClient;
import ru.yandex.webmaster3.core.worker.task.WorkerTaskPriority;
import ru.yandex.webmaster3.storage.delurl.DelUrlRequestsService;
import ru.yandex.webmaster3.storage.delurl.DelUrlService;

/**
 * Created by Oleg Bazdyrev on 09/10/2017.
 */
@WriteAction
@Category("delurl")
@Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class AddDelurlMassRequestAction extends Action<AddDelurlMassRequestRequest, AddDelurlMassRequestResponse> {
    private final DelUrlService delurlService;
    private final DelUrlRequestsService delurlRequestsService;
    private final WorkerClient workerClient;

    @Override
    public AddDelurlMassRequestResponse process(AddDelurlMassRequestRequest request) {
        DateTime now = DateTime.now();

        // проверим, что указан хост и пользователь, иначе ограничение на одну запись
        if (request.getUserId() == 0L || request.getHostId() == null) {
            return processSingleUrl(request);
        }

        DailyQuotaUtil.QuotaUsage quotaUsage = delurlRequestsService.getQuotaUsage(request.getHostId(), request.getType());
        // проверим размер запроса
        if (request.getUrls().size() > quotaUsage.getTodayQuota()) {
            return new AddDelurlMassRequestResponse.RequestTooBigResponse();
        }
        // проверим квоту
        if (quotaUsage.getQuotaRemain() - request.getUrls().size() < 0) {
            return new AddDelurlMassRequestResponse.QuotaExceededResponse();
        }
        // разгребаем все переданные урлы
        List<String> invalidUrls = new ArrayList<>();
        List<DelUrlRequest> requests = new ArrayList<>();
        WebmasterHostId hostId = request.getHostId();
        for (String urlString : new LinkedHashSet<>(request.getUrls())) {
            try {
                if (StringUtils.isBlank(urlString)) {
                    continue;
                }
                String relativeUrl = processUrl(hostId, urlString, request.getType());
                if (relativeUrl != null) {
                    DelUrlRequest delurlRequest = new DelUrlRequest(UUIDs.timeBased(), hostId, relativeUrl,
                            DelurlState.NEW, request.getType(), now, now,
                            true, 0, false, request.getUserId(), request.getBalancerRequestId().toString(), 0, null);
                    requests.add(delurlRequest);
                } else {
                    invalidUrls.add(urlString);
                }
            } catch (Exception e) {
                invalidUrls.add(urlString);
            }
        }
        if (requests.isEmpty()) {
            return new AddDelurlMassRequestResponse.NoValidUrlsResponse();
        }
        if (request.getType() == DelurlType.PREFIX) {
            return processPrefix(requests, quotaUsage);
        }
        // сохраняем и отправляем воркеру
        delurlRequestsService.addBatch(requests);
        workerClient.enqueueTask(new ProcessDelurlRequestTaskData(hostId, WorkerTaskPriority.HIGH, requests));
        return new AddDelurlMassRequestResponse.NormalResponse(quotaUsage.getTodayQuota(),
                quotaUsage.getQuotaUsed() + requests.size(),
                quotaUsage.getQuotaRemain() - requests.size(), requests,
                invalidUrls.isEmpty() ? null : AddDelurlMassRequestResponse.DelurlWarningType.DELURL__INVALID_URLS,
                invalidUrls);
    }

    private String processUrl(WebmasterHostId hostId, String urlString, DelurlType type) throws Exception {
        String result = IdUtils.toRelativeUrl(hostId, urlString, false);
        if (result == null || result.contains("\t")) {
            return null;
        }
        if (type == DelurlType.PREFIX) {
            // проверим, что урл оканчивается на слеш или ?
            if (urlString.endsWith("/")) {
                return result;
            } else if (urlString.endsWith("?")) {
                return result + "?";
            }
            return null;
        }
        return result;
    }

    private AddDelurlMassRequestResponse processSingleUrl(AddDelurlMassRequestRequest request) {
        if (request.getUrls().size() != 1) {
            return new AddDelurlMassRequestResponse.NoValidUrlsResponse();
        }
        String url = request.getUrls().get(0);
        WebmasterHostId hostId;
        String relativeUrl;
        try {
            hostId = IdUtils.urlToHostId(url);
            relativeUrl = IdUtils.toRelativeUrl(hostId, url, false);
            if (relativeUrl == null) {
                return new AddDelurlMassRequestResponse.NoValidUrlsResponse();
            }
        } catch (Exception e) {
            return new AddDelurlMassRequestResponse.NoValidUrlsResponse();
        }
        // проверим квоту
        DailyQuotaUtil.QuotaUsage quotaUsage = delurlRequestsService.getQuotaUsage(hostId, DelurlType.URL);
        if (quotaUsage.getQuotaRemain() - request.getUrls().size() < 0) {
            return new AddDelurlMassRequestResponse.QuotaExceededResponse();
        }

        DelUrlRequest result;
        try {
            result = delurlService.processUrl(hostId, relativeUrl, request.getUserIp(),
                    request.getYandexUid(), request.getUserId(), request.getBalancerRequestId().toString(), true);
        } catch (RecrawlServiceException e) {
            throw new WebmasterException("Recrawl error",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), e.getMessage()), e);
        }
        delurlRequestsService.add(result);
        if (result.getState() == DelurlState.IN_PROGRESS || result.getState() == DelurlState.ACCEPTED) {
            return new AddDelurlMassRequestResponse.NormalResponse(quotaUsage.getTodayQuota(),
                    quotaUsage.getQuotaUsed(), quotaUsage.getQuotaRemain(),
                    Lists.newArrayList(result), null, null);
        } else {
            return new AddDelurlMassRequestResponse.NoReasonToDeleteResponse();
        }
    }

    private AddDelurlMassRequestResponse processPrefix(List<DelUrlRequest> requests,
                                                       DailyQuotaUtil.QuotaUsage quotaUsage) {
        if (requests.size() != 1) {
            throw new WebmasterException("Only 1 prefix is supported",
                    new WebmasterErrorResponse.IllegalParameterValueResponse(getClass(), "urls", null));
        }
        try {
            DelUrlRequest result = delurlService.processUrl(requests.get(0));
            if (result.getState() == DelurlState.ACCEPTED) {
                delurlRequestsService.add(result);
                return new AddDelurlMassRequestResponse.NormalResponse(quotaUsage.getTodayQuota(),
                        quotaUsage.getQuotaUsed() + requests.size(), quotaUsage.getQuotaRemain() - requests.size(),
                        Lists.newArrayList(result), null, null);
            } else {
                return new AddDelurlMassRequestResponse.NoReasonToDeleteResponse();
            }
        } catch (RecrawlServiceException e) {
            // такого быть не может
            throw new WebmasterException("Impossible exception",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Impossible error"), e);
        }
    }
}
