package ru.yandex.wmconsole.servantlet;

import org.jdom.Document;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.common.framework.core.ServRequest;
import ru.yandex.common.framework.core.ServResponse;
import ru.yandex.webmaster.common.urltree.YandexSearchShard;
import ru.yandex.wmconsole.data.XMLTestQueryTypeEnum;
import ru.yandex.wmconsole.data.wrappers.StringWrapper;
import ru.yandex.wmconsole.util.XMLSearchRequests;
import ru.yandex.wmtools.common.data.xmlsearch.XmlSearchRequest;
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.servantlet.AuthenticationServantlet;
import ru.yandex.wmtools.common.service.JDomXmlSearchResultParser;
import ru.yandex.wmtools.common.service.XmlSearchService;

/**
 * @author avhaliullin
 */
public class XMLTestQueryServantlet extends AuthenticationServantlet {
    private static final String PARAM_SEARCH_SHARD = "search_shard";
    private static final String PARAM_QUERY_TYPE = "query_type";
    private static final String PARAM_HOST_NAME = "host_name";
    private static final String PARAM_FROM_HOST = "from_host";
    private static final String PARAM_PATH = "path";
    private static final String PARAM_LAST_DAYS = "last_days";
    private static final String PARAM_PAGE_NUM = "page_num";
    private static final String PARAM_PAGE_SIZE = "page_size";
    private static final String PARAM_QUERY = "query";
    private static final String PARAM_REGION = "region";

    private static final String TAG_RESPONSE = "response";

    private XmlSearchService xmlSearchService;
    private XmlSearchService uaXmlSearchService;
    private XmlSearchService comXmlSearchService;
    private XmlSearchService trXmlSearchService;

    @Override
    protected void doProcess(ServRequest req, ServResponse res, long userId) throws UserException, InternalException {
        String shardName = getRequiredStringParam(req, PARAM_SEARCH_SHARD);
        String queryTypeName = getRequiredStringParam(req, PARAM_QUERY_TYPE);
        YandexSearchShard shard = YandexSearchShard.getByName(shardName);
        XMLTestQueryTypeEnum queryType = XMLTestQueryTypeEnum.getByName(queryTypeName);

        if (shard == null) {
            throw new UserException(UserProblem.ILLEGAL_PARAM_VALUE, "Unknown search shard", PARAM_SEARCH_SHARD, shardName);
        }
        if (queryType == null) {
            throw new UserException(UserProblem.ILLEGAL_PARAM_VALUE, "Unknown XML test query type", PARAM_QUERY_TYPE, queryTypeName);
        }

        XmlSearchRequest request;
        switch (queryType) {
            case LINKS_COUNT:
                request = XMLSearchRequests.createLinksCount(
                        getHostName(req),
                        getPath(req),
                        getLastDays(req));
                break;
            case INDEXED_URLS:
                request = XMLSearchRequests.createIndexedUrls(
                        getHostName(req),
                        getPath(req),
                        getLastDays(req),
                        getPageNum(req),
                        getPageSize(req)
                );
                break;
            case LINKS_GROUPS:
                request = XMLSearchRequests.createLinksGroups(
                        getHostName(req),
                        getPath(req),
                        getLastDays(req),
                        getPageNum(req),
                        getPageSize(req)
                );
                break;
            case LINKS_FROM_HOST:
                request = XMLSearchRequests.createLinksFromHost(
                        getHostName(req),
                        getPath(req),
                        getRequiredStringParam(req, PARAM_FROM_HOST),
                        getLastDays(req),
                        getPageNum(req),
                        getPageSize(req)
                );
                break;
            case FAST_INDEX_COUNT:
                request = XMLSearchRequests.createFastIndexCount(
                        getHostName(req),
                        getPath(req)
                );
                break;
            case INTERNAL_LINKS:
                request = XMLSearchRequests.createInternalLinks(
                        getHostName(req),
                        getPath(req),
                        getLastDays(req),
                        getPageNum(req),
                        getPageSize(req)
                );
                break;
            case REGION_SEARCH:
                request = XMLSearchRequests.createRegionsSearch(
                        getRequiredStringParam(req, PARAM_QUERY),
                        getRequiredLongParam(req, PARAM_REGION),
                        getPageNum(req),
                        getPageSize(req)
                );
                break;
            case SERP_LINKS:
                request = XMLSearchRequests.createSerpLinks(
                        getHostName(req),
                        getPath(req)
                );
                break;
            default:
                throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "Unknown XML test query type: " + queryType);
        }

        XmlSearchService service = pickXmlSearchService(shard);

        Document doc = service.callXMLSearch(request, new JDomXmlSearchResultParser());
        String resultString = new XMLOutputter(Format.getPrettyFormat()).outputString(doc);
        res.addData(new StringWrapper(resultString, TAG_RESPONSE));
    }

    private String getHostName(ServRequest req) throws UserException {
        return getRequiredStringParam(req, PARAM_HOST_NAME);
    }

    private String getPath(ServRequest req) throws UserException {
        return getRequiredStringParam(req, PARAM_PATH);
    }

    private Integer getLastDays(ServRequest req) throws UserException {
        return getIntParam(req, PARAM_LAST_DAYS);
    }

    private int getPageNum(ServRequest req) throws UserException {
        return getIntParam(req, PARAM_PAGE_NUM, 0);
    }

    private int getPageSize(ServRequest req) throws UserException {
        return getIntParam(req, PARAM_PAGE_SIZE, 25);
    }

    private int getIntParam(ServRequest req, String param, int defaultValue) throws UserException {
        Integer res = getIntParam(req, param);
        return res == null ? defaultValue : res;
    }

    private XmlSearchService pickXmlSearchService(YandexSearchShard shard) throws InternalException {
        XmlSearchService result;
        switch (shard) {
            case COM:
                result = comXmlSearchService;
                break;
            case COM_TR:
                result = trXmlSearchService;
                break;
            case RU:
                result = xmlSearchService;
                break;
            case UA:
                result = uaXmlSearchService;
                break;
            default:
                throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "Unknown search shard: " + shard);
        }
        return result;
    }

    @Required
    public void setXmlSearchService(XmlSearchService xmlSearchService) {
        this.xmlSearchService = xmlSearchService;
    }

    @Required
    public void setUaXmlSearchService(XmlSearchService uaXmlSearchService) {
        this.uaXmlSearchService = uaXmlSearchService;
    }

    @Required
    public void setComXmlSearchService(XmlSearchService comXmlSearchService) {
        this.comXmlSearchService = comXmlSearchService;
    }

    @Required
    public void setTrXmlSearchService(XmlSearchService trXmlSearchService) {
        this.trXmlSearchService = trXmlSearchService;
    }
}
