package ru.yandex.wmconsole.servantlet.sitemaps;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.common.framework.core.ServRequest;
import ru.yandex.common.framework.core.ServResponse;
import ru.yandex.common.framework.pager.Pager;
import ru.yandex.common.util.db.OrderByClause;
import ru.yandex.wmconsole.data.ErrorUrlInfoCorrespondence;
import ru.yandex.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.data.info.ErrorCountInfo;
import ru.yandex.wmconsole.data.info.ErrorUrlInfo;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmconsole.data.info.SitemapNavigationInfo;
import ru.yandex.wmconsole.data.wrappers.ErrorCountInfoWrapper;
import ru.yandex.wmconsole.data.wrappers.ErrorUrlInfoWrapper;
import ru.yandex.webmaster.viewer.sitemap.CurrentSitemapService;
import ru.yandex.wmconsole.servantlet.WMCAuthorizedHostOperationServantlet;
import ru.yandex.wmconsole.service.ErrorInfoService;
import ru.yandex.wmconsole.service.HostDbHostInfoService;
import ru.yandex.wmconsole.util.HostElementWrapper;
import ru.yandex.wmconsole.util.ServantletUtil;
import ru.yandex.wmconsole.util.UrlErrorOrGroup;
import ru.yandex.wmconsole.util.XmlUtil;
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.util.TimeFilter;
import ru.yandex.wmtools.common.util.XmlConvertableCollectionWrapper;

/**
 * @author Andrey Mima (amima@yandex-team.ru)
 */
public class SitemapErrorUrlsServantlet extends WMCAuthorizedHostOperationServantlet {
    private static final Logger log = LoggerFactory.getLogger(SitemapErrorUrlsServantlet.class);

    private static final String PARAM_SITEMAP = "sitemap";
    private static final String PARAM_INDEX = "index";
    private static final String PARAM_CODE = "code";
    private static final String PARAM_LAST = "last";

    private ErrorInfoService errorInfoService;
    private CurrentSitemapService currentSitemapService;
    private HostDbHostInfoService hostDbHostInfoService;

    @Override
    protected void doProcess(ServRequest req, ServResponse res, long userId) throws UserException, InternalException {
        // входной параметр всегда для версии в поиске
        long sitemapId = getRequiredLongParam(req, PARAM_SITEMAP);
        Long indexId = getLongParam(req, PARAM_INDEX);
        int code = getRequiredIntParam(req, PARAM_CODE);

        BriefHostInfo briefHostInfo = getHostInfo(req, true, userId, true, true, res, true, true);
        if (briefHostInfo == null) {
            return;
        }
        final HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(briefHostInfo.getName());

        // Ссылки на содержимое sitemap index и статус sitemap показываем на последнюю загруженную версию
        // Ссылку на группы исключенных страниц показываем из входных параметров (sitemap_id, 2)
        final Long latestId = currentSitemapService.getLatestSitemapIdBySitemapIdInSearch(hostDbHostInfo, sitemapId);
        final SitemapNavigationInfo navigationInfo = currentSitemapService.getNavigationInfo(hostDbHostInfo, latestId, indexId);
        try {
            if (navigationInfo != null) {
                res.addData(XmlUtil.xmlConvertable(navigationInfo));
            }
        } catch (JAXBException e) {
            log.error("Jaxb marshalling error ", e);
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "Jaxb marshalling error", e);
        }

        UrlErrorOrGroup error = new UrlErrorOrGroup(code);

        Pager pager = createOutputStrategy(req).createPager();
        OrderByClause order = new OrderByClause(req, ErrorInfoService.FIELD_URL, true, new ErrorUrlInfoCorrespondence());

        Calendar now = Calendar.getInstance();

        String after = null;
        String before = null;
        TimeFilter timeFilter = null;
        boolean needFilter = false;
        if ("7".equals(getStringParam(req, PARAM_LAST))) {
            now.add(Calendar.WEEK_OF_YEAR, -1);
            needFilter = true;
        } else if ("14".equals(getStringParam(req, PARAM_LAST))) {
            now.add(Calendar.WEEK_OF_YEAR, -2);
            needFilter = true;
        } else if ("21".equals(getStringParam(req, PARAM_LAST))) {
            now.add(Calendar.WEEK_OF_YEAR, -3);
            needFilter = true;
        }

        if ((after != null) || (before != null)) {
            try {
                timeFilter = TimeFilter.create(after, before);
            } catch (NumberFormatException e) {
                throw new UserException(UserProblem.ILLEGAL_VALUE_TYPE, "failed to parse params", e);
            }
        } else if (needFilter) {
            Integer year = now.get(Calendar.YEAR);
            Integer month = now.get(Calendar.MONTH) + 1;
            Integer day = now.get(Calendar.DATE);
            after = year.toString() + "/" + month.toString() + "/" + day.toString();
        }

        if (timeFilter == null) {
            try {
                timeFilter = TimeFilter.create(after, null, "yyyy/MM/dd");
            } catch (ParseException e) {
                throw new UserException(UserProblem.ILLEGAL_VALUE_TYPE, "failed to parse date", e);
            }
        }

        final List<ErrorUrlInfo> errorUrlInfos = getSafeUrlsWithErrorByCode(userId, briefHostInfo, error, pager, order, timeFilter, sitemapId);

        XmlConvertableCollectionWrapper wrappedUrlErrors =
                XmlConvertableCollectionWrapper.wrap(
                        errorUrlInfos,
                        ErrorUrlInfoWrapper.class,
                        "url-errors-for-code",
                        "code",
                        Integer.toString(code)
                );
        res.addData(new HostElementWrapper(wrappedUrlErrors, briefHostInfo));

        ErrorCountInfo errorCountInfo = errorInfoService.calcErrorCountInfo(briefHostInfo, error, timeFilter, false, false, sitemapId);
        res.addData(new ErrorCountInfoWrapper(errorCountInfo));

        res.addData(pager);
    }

    private List<ErrorUrlInfo> getSafeUrlsWithErrorByCode(long userId, BriefHostInfo hostInfo, UrlErrorOrGroup error, Pager pager, OrderByClause order, final TimeFilter timeFilter, final Long sitemapId) throws InternalException {
        List<ErrorUrlInfo> errorUrlInfos;

        if (ServantletUtil.isErrorCodeSafeToShow(error)) {
            errorUrlInfos = errorInfoService.getUrlsWithErrorsByCode(pager, order, userId, hostInfo, error, timeFilter, false, false, sitemapId);
        } else {
            errorUrlInfos = Collections.emptyList();
        }

        return errorUrlInfos;
    }

    @Required
    public void setErrorInfoService(ErrorInfoService errorInfoService) {
        this.errorInfoService = errorInfoService;
    }

    @Required
    public void setCurrentSitemapService(CurrentSitemapService currentSitemapService) {
        this.currentSitemapService = currentSitemapService;
    }

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