package ru.yandex.wmconsole.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import wmc.stubs.Stubs;

import ru.yandex.wmconsole.data.SitemapAnalysisResult;
import ru.yandex.wmconsole.data.info.AnalysisResult;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.error.UserProblem;
import ru.yandex.wmtools.common.service.LightDispatcherHttpService;

/**
 * @author avhaliullin
 */
public class DispatcherHttpService extends LightDispatcherHttpService {
    private static final Logger log = LoggerFactory.getLogger(DispatcherHttpService.class);

    private static final long MAX_SITEMAP_SIZE = 10*1024*1024;
    private static final int SOCKET_TIMEOUT_MILLIS = 30 * 1000;

    private HostDbHostInfoService hostDbHostInfoService;
    private Boolean supportMode;

    public AnalysisResult analyzeRobotsTxt(final String robotsTxt, final String[] urls) throws InternalException {
        Stubs.analysis_result_msg.Builder ans;
        try {
            ans = Stubs.analysis_result_msg.newBuilder().mergeFrom(httpPostResponse("analyzerobotstxt", createParams()
                    .addParam("robotstxt", robotsTxt)
                    .addParam("urls", getDelimitedWith(urls, "\n")), SOCKET_TIMEOUT_MILLIS));
        } catch (IOException e) {
            throw assertServantIsUnavailable(e);
        }
        return new AnalysisResult(ans.build());
    }

    public void uploadHostData(final String hostname, final long hostId, @NotNull final Date updateTime) throws InternalException {
        log.debug("Upload host data called");
        if (supportMode) {
            log.debug("upload host data in support mode is cancelled");
            throw new InternalException(InternalProblem.PROCESSING_ERROR, "upload host data in support mode is not allowed");
        }
        if (hostDbHostInfoService.checkHostInProgress(new HostDbHostInfo(hostId, hostname))) {
            log.debug("Host already in progress");
            return;
        }
        final long updateTimestamp = updateTime.getTime() / 1000l;
        log.debug("Sending query to dispatcher");
        Stubs.long_err_msg.Builder ans;
        try {
            final Params params = createParams().addParam("hostname", hostname).addParam("updatetime", updateTimestamp);
            ans = Stubs.long_err_msg.newBuilder().mergeFrom(httpPostResponse("uploadhostdata", params,
                    SOCKET_TIMEOUT_MILLIS));
        } catch (IOException e) {
            throw assertServantIsUnavailable(e);
        }

        if (ans.hasErr()) {
            if (Stubs.error_code.HostInProcess.equals(ans.getErr().getVal())) {
                log.warn("Caught HostInProgress error");
                return;
            }
            throw new InternalException(InternalProblem.PROCESSING_ERROR, ans.getErr().getVal().name() + ": " + ans.getErr().getDesc());
        } else {
            log.debug("Host data for " + hostname + " has been uploaded");
        }

        if (!ans.hasVal()) {
            throw assertServantIsUnavailable();
        }
        log.debug("Upload host data: the answer is \"" + ans.getVal().getVal() + "\"");
    }

    public SitemapAnalysisResult analyzeSitemapBytes(final byte[] data) throws InternalException, UserException {
        if (data.length > MAX_SITEMAP_SIZE) {
            throw new UserException(UserProblem.SITEMAP_TOO_LONG, "Sitemap limit exceeded");
        }

        try {
            log.debug(URLEncoder.encode(new String(Arrays.copyOfRange(data, 5, Math.min(data.length, 256))), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
        }

        final Stubs.stm_result_msg.Builder ans;
        try {
            ans = Stubs.stm_result_msg.newBuilder().mergeFrom(
                    httpPostByteArrayResponse("analyzeSitemap",
                            data, SOCKET_TIMEOUT_MILLIS, true));
        } catch (IOException e) {
            throw assertServantIsUnavailable(e);
        }
        return new SitemapAnalysisResult(ans.build());
    }

    /**
     * Пересчитать тренды на основе имеющейся истории
     *
     * @param hostname              имя хоста
     * @throws InternalException
     */
    public void updateTrendInfo(final String hostname) throws InternalException {
        log.debug("Update trend info called");
        if (supportMode) {
            log.debug("update trend info in support mode is cancelled");
            throw new InternalException(InternalProblem.PROCESSING_ERROR, "update trend info in support mode is not allowed");
        }
        Stubs.long_err_msg.Builder ans;
        try {
            ans = Stubs.long_err_msg.newBuilder().mergeFrom(httpPostResponse("updatetrendinfo", createParams()
                    .addParam("hostname", hostname), 1000 * 60 * 60 * 12));
        } catch (IOException e) {
            throw assertServantIsUnavailable(e);
        }

        if (ans.hasErr()) {
            throw new InternalException(InternalProblem.PROCESSING_ERROR, ans.getErr().getVal().name() + ": " + ans.getErr().getDesc());
        } else {
            log.debug("Trends for " + hostname + " have been updated");
        }

        if (!ans.hasVal()) {
            throw assertServantIsUnavailable();
        }
        log.debug("Update trend info: the answer is \"" + ans.getVal().getVal() + "\"");
    }

    @Required
    public void setHostDbHostInfoService(HostDbHostInfoService hostDbHostInfoService) {
        this. hostDbHostInfoService = hostDbHostInfoService;
    }

    @Required
    public void setSupportMode(Boolean supportMode) {
        this.supportMode = supportMode;
    }
}
