package ru.yandex.wmconsole.servantlet.sitemaps;

import java.util.Date;

import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

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.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmconsole.data.info.SitemapInfo;
import ru.yandex.wmconsole.data.info.SitemapNavigationInfo;
import ru.yandex.wmconsole.data.sitemaps.SitemapSourceEnum;
import ru.yandex.wmconsole.data.sitemaps.SitemapTypeEnum;
import ru.yandex.webmaster.viewer.sitemap.CurrentSitemapService;
import ru.yandex.wmconsole.servantlet.WMCAuthorizedHostOperationServantlet;
import ru.yandex.wmconsole.service.HostDbHostInfoService;
import ru.yandex.wmconsole.service.error.WMCUserProblem;
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;

/**
 * Сервантлет для отображения статуса файла sitemap
 *
 * Показывает статусы для двух версий файла (последняя загруженная и используемая в поиске)
 *
 * User: Alexey Zakharov <azakharov@yandex-team.ru>
 * Date: 14.02.12
 */
public class SitemapIndexStatusServantlet extends WMCAuthorizedHostOperationServantlet {
    private static final Logger log = LoggerFactory.getLogger(SitemapIndexStatusServantlet.class);

    private static final String PARAM_SITEMAP = "sitemap";
    private static final String PARAM_SOURCE = "source";
    private static final String PARAM_INDEX = "index";

    private HostDbHostInfoService hostDbHostInfoService;
    private CurrentSitemapService currentSitemapService;

    @Override
    protected void doProcess(ServRequest req, ServResponse res, long userId) throws UserException, InternalException {

        final BriefHostInfo briefHostInfo = getHostInfoAndVerify(req, userId);
        final HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(briefHostInfo.getName());
        final long sitemapId = getRequiredLongParam(req, PARAM_SITEMAP);
        final int src = getRequiredIntParam(req, PARAM_SOURCE);
        final SitemapSourceEnum source = SitemapSourceEnum.R.fromValueOrNull(src);
        if (source == null) {
            log.error("Sitemap not found");
            throw new UserException(UserProblem.ILLEGAL_PARAM_VALUE, "sitemap not found");
        }
        final Long indexId = getLongParam(req, PARAM_INDEX);

        final SitemapInfo newInfo;
        if (source == SitemapSourceEnum.LATEST) {
            newInfo = currentSitemapService.getSitemapInfo(hostDbHostInfo, sitemapId, indexId);
            if (newInfo == null) {
                log.error("Sitemap not found (sitemap_id=" + sitemapId + ", source=" + source + ")");
                throw new UserException(WMCUserProblem.SITEMAP_NOT_OWNED,
                        "Sitemap not found (sitemap_id=" + sitemapId + ", source=" + source + ")");
            }
            final SitemapTypeEnum type = newInfo.getSitemapType();
            if (!SitemapTypeEnum.SITEMAPINDX.equals(type)) {
                throw new UserException(WMCUserProblem.BAD_FORMAT, "Sitemapindex expected but " + type + " + found");
            }
        } else {
            // статус sitemapindex  смотрим только по последней загруженной версии
            throw new UnsupportedOperationException(
                    "Unsupported source value " + source + ". Only " + SitemapSourceEnum.LATEST + " allowed");
        }

        final SitemapNavigationInfo navigationInfo =
                currentSitemapService.getNavigationInfo(hostDbHostInfo, sitemapId, 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);
        }

        final SitemapIndexStatusInfo wrapper = getSitemapIndexStatusInfo(newInfo);
        try {
            res.addData(XmlUtil.xmlConvertable(wrapper));
        } catch (JAXBException e) {
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "XML marshaling error", e);
        }
    }

    private SitemapIndexStatusInfo getSitemapIndexStatusInfo(final SitemapInfo info) {
        if (info == null) {
            return null;
        }

        return new SitemapIndexStatusInfo(
                info.getId(),
                info.getUrl(),
                info.getProccessedDate(),
                info.getUrlsCount(),
                info.getUrlErrorsCount(),
                info.getXmlErrorsCount(),
                info.getWarningsCount());
    }

    @XmlRootElement(name = "sitemapindex")
    public static class SitemapIndexStatusInfo {
        private long sitemapId;
        private String url;
        private Date date;
        private Integer urlCount;
        private Integer urlErrorCount;
        private Integer errorCount;
        private Integer warningCount;

        public SitemapIndexStatusInfo() {
            // for JAXB
        }

        public SitemapIndexStatusInfo(long sitemapId, String url, Date date, Integer urlCount,
                                      Integer urlErrorCount, Integer errorCount, Integer warningCount) {
            this.sitemapId = sitemapId;
            this.url = url;
            this.date = date;
            this.urlCount = urlCount;
            this.urlErrorCount = urlErrorCount;
            this.errorCount = errorCount;
            this.warningCount = warningCount;
        }

        @XmlElement(name = "sitemap-id")
        public long getSitemapId() {
            return sitemapId;
        }

        @XmlElement(name = "url")
        public String getUrl() {
            return url;
        }

        @XmlElement(name = "date")
        public Date getDate() {
            return date;
        }

        @XmlElement(name = "url-count")
        public Integer getUrlCount() {
            return urlCount;
        }

        @XmlElement(name = "url-error-count")
        public Integer getUrlErrorCount() {
            return urlErrorCount;
        }

        @XmlElement(name = "error-count")
        public Integer getErrorCount() {
            return errorCount;
        }

        @XmlElement(name = "warning-count")
        public Integer getWarningCount() {
            return warningCount;
        }
    }

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

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