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.MergedSitemapInfo;
import ru.yandex.wmconsole.data.info.SitemapNavigationInfo;
import ru.yandex.wmconsole.data.sitemaps.SitemapSourceEnum;
import ru.yandex.webmaster.viewer.sitemap.CurrentSitemapService;
import ru.yandex.wmconsole.servantlet.WMCAuthorizedHostOperationServantlet;
import ru.yandex.wmconsole.service.HostDbHostInfoService;
import ru.yandex.webmaster.common.sitemap.SitemapService;
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 SitemapStatusServantlet extends WMCAuthorizedHostOperationServantlet {
    private static final Logger log = LoggerFactory.getLogger(SitemapStatusServantlet.class);

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

    private SitemapService sitemapService;
    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 MergedSitemapInfo newInfo;
        final MergedSitemapInfo oldInfo;
        final SitemapNavigationInfo navigationInfo;
        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 + ")");
            }
            navigationInfo = currentSitemapService.getNavigationInfo(hostDbHostInfo, sitemapId, indexId);
            oldInfo = sitemapService.getSitemapInfoByUrl(hostDbHostInfo, newInfo);
            if (oldInfo == null) {
                // Файл может быть найден через robots_txt,
                // присутствовать в tbl_sitemaps_cur, но отсутствовать в tbl_sitemaps
                log.info("Sitemap file is not used by robot");
            }
        } else {
            throw new UnsupportedOperationException("Unsupported source value " + source);
        }

        final SitemapInfoWrapper wrapper = new SitemapInfoWrapper(
                getStatusInfo(oldInfo, false),
                getStatusInfo(newInfo, true)
        );

        try {
            res.addData(XmlUtil.xmlConvertable(wrapper));
            if (navigationInfo != null) {
                res.addData(XmlUtil.xmlConvertable(navigationInfo));
            }
        } catch (JAXBException e) {
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "XML marshaling error", e);
        }
    }

    /**
     * Создает представление статуса файла sitemap.
     *
     * @param info          исходная информация
     * @param isLatest      выдавать представление для последней загруженной версии (true) или
     *                      версии в поиске (false)
     * @return представление статуса файла sitemap
     */
    private SitemapStatusInfo getStatusInfo(final MergedSitemapInfo info, final boolean isLatest) {
        if (info == null) {
            return null;
        }

        final Date date;
        if (isLatest) {
            // Дата processed_on должна быть
            date = info.getProccessedDate();
        } else {
            // Дата real_processed_on может отсутствовать, если в файле есть ошибки
            date = info.getRealProcessedDate() != null ? info.getRealProcessedDate() : info.getProccessedDate();
        }

        if (date == null) {
            // Не выдаем информацию по sitemap, если нет даты
            return null;
        }

        return new SitemapStatusInfo(
                info.getId(),
                info.getUrl(),
                date,
                info.getUrlsCount(),
                info.getUrlErrorsCount(),
                info.getXmlErrorsCount(),
                info.getWarningsCount(),
                info.getSource() != null? info.getSource().getId() : null);
    }

    public static class SitemapStatusInfo {
        private long sitemapId;
        private Integer source;
        private String url;
        private Date date;
        private Integer urlCount;
        private Integer urlErrorsCount;
        private Integer errorCount;
        private Integer warningsCount;

        public SitemapStatusInfo() {
            // for JAXB
        }

        public SitemapStatusInfo(long sitemapId, String url, Date date, Integer urlCount, Integer urlErrorsCount, Integer errorCount, Integer warningsCount, Integer source) {
            this.sitemapId = sitemapId;
            this.url = url;
            this.date = date;
            this.urlCount = urlCount;
            this.urlErrorsCount = urlErrorsCount;
            this.errorCount = errorCount;
            this.warningsCount = warningsCount;
            this.source = source;
        }

        @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 getUrlErrorsCount() {
            return urlErrorsCount;
        }

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

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

        @XmlElement(name = "source")
        public Integer getSource() {
            return source;
        }
    }

    @XmlRootElement(name = "sitemaps")
    public static class SitemapInfoWrapper {
        private SitemapStatusInfo oldInfo;
        private SitemapStatusInfo newInfo;

        public SitemapInfoWrapper() {
            // for JAXB
        }

        public SitemapInfoWrapper(SitemapStatusInfo oldInfo, SitemapStatusInfo newInfo) {
            this.oldInfo = oldInfo;
            this.newInfo = newInfo;
        }

        @XmlElement(name = "in-search")
        public SitemapStatusInfo getOldInfo() {
            return oldInfo;
        }

        @XmlElement(name = "current")
        public SitemapStatusInfo getNewInfo() {
            return newInfo;
        }
    }

    @Required
    public void setSitemapService(SitemapService sitemapService) {
        this.sitemapService = sitemapService;
    }

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

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