package ru.yandex.wmconsole.servantlet.robotstxt;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

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.AnalysisResult;
import ru.yandex.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.data.info.ErrorInfo;
import ru.yandex.wmconsole.data.info.FormatErrorType;
import ru.yandex.wmconsole.data.info.UrlAllowInfo;
import ru.yandex.wmconsole.data.wrappers.StringWrapper;
import ru.yandex.wmconsole.servantlet.WMCAuthorizedHostOperationServantlet;
import ru.yandex.wmconsole.service.DispatcherHttpService;
import ru.yandex.wmconsole.service.RobotsTxtService;
import ru.yandex.wmconsole.service.error.WMCUserProblem;
import ru.yandex.wmconsole.util.HostElementWrapper;
import ru.yandex.wmconsole.util.XmlUtil;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;

/**
 * Loads robots.txt file from a users host or analyzes it.
 *
 * @author ailyin
 */
public class AnalyzeRobotsTxtServantlet extends WMCAuthorizedHostOperationServantlet {
    private static final Logger log = LoggerFactory.getLogger(AnalyzeRobotsTxtServantlet.class);

    private RobotsTxtService robotsTxtService;
    private DispatcherHttpService dispatcherHttpService;

    @Override
    protected void doProcess(ServRequest req, ServResponse res, long userId)
            throws UserException, InternalException {
        log.debug("AnalyzeRobotsTxt: Started");

        BriefHostInfo briefHostInfo = getHostInfoAndVerify(req, userId);
        res.addData(new HostElementWrapper(null, briefHostInfo));

        Boolean loadOnlyParam = getBooleanParam(req, RobotsTxtHelper.PARAM_ONLY_LOAD);
        boolean loadOnly = (loadOnlyParam != null) && (loadOnlyParam);

        URL hostname;
        try {
            hostname = SupportedProtocols.getURL(briefHostInfo.getName());
        } catch (MalformedURLException e) {
            throw new AssertionError("invalid hostname in a database!");
        } catch (URISyntaxException e) {
            throw new AssertionError("invalid hostname in a database!");
        } catch (SupportedProtocols.UnsupportedProtocolException e) {
            throw new AssertionError("there is a host in a database with unsupported protocol!");
        }

        String robotsTxtContent = getAndOutputRobotsTxtContent(req, res, hostname, loadOnly);
        if (!loadOnly) {
            outputAnalysisResult(req, hostname, robotsTxtContent, res);
        }

        log.debug("AnalyzeRobotsTxt: Finished");
    }

    private String getAndOutputRobotsTxtContent(ServRequest req, ServResponse res, URL hostname, boolean loadOnly)
            throws UserException, InternalException
    {
        String robotsTxtContent = req.getParam(RobotsTxtHelper.PARAM_ROBOTSTXT, true);
        log.debug("robotsTxtContent='" + robotsTxtContent + "'");

        try {
            // robotsTxtContent==null only when the page is opened for the first time
            if ((robotsTxtContent == null) || loadOnly) { // need to download
                robotsTxtContent = robotsTxtService.getRobotsTxtContent(hostname);
            }
        } finally {
            if (robotsTxtContent != null) {
                res.addData(new StringWrapper(XmlUtil.stripInvalidXMLCharacters(robotsTxtContent), "robots-txt"));
            }
        }
        return robotsTxtContent;
    }

    private void outputAnalysisResult(ServRequest req, URL hostname, String robotsTxtContent, ServResponse res)
            throws InternalException, UserException {
        String[] urls = splitInput(req.getParam(RobotsTxtHelper.PARAM_URLS));
        if (urls.length > RobotsTxtHelper.MAX_URLS_COUNT) {
            throw new UserException(WMCUserProblem.TOO_MANY_URLS, "too many urls");
        }

        List<UrlAllowInfo> allowInfos = Arrays.asList(new UrlAllowInfo[urls.length]);
        List<String> validUrls = RobotsTxtHelper.getValidUrls(hostname, urls, allowInfos);
        AnalysisResult result = dispatcherHttpService.analyzeRobotsTxt(robotsTxtContent,
                validUrls.toArray(new String[validUrls.size()]));

        String[] robotsTxtLines = RobotsTxtHelper.splitRobotsTxt(robotsTxtContent);

        if (robotsTxtContent.length() > RobotsTxtHelper.MAX_ROBOTS_TXT_SIZE) {
            int failLine = RobotsTxtHelper.countOccurences(robotsTxtContent.substring(0, RobotsTxtHelper.MAX_ROBOTS_TXT_SIZE), '\n') + 1;
            res.addData(RobotsTxtHelper.getErrorsData(
                    RobotsTxtHelper.addToRightPosition(result.getErrors(), new ErrorInfo(FormatErrorType.ERR_ROBOTS_HUGE, failLine)),
                    robotsTxtLines));
        } else {
            res.addData(RobotsTxtHelper.getErrorsData(result.getErrors(), robotsTxtLines));
        }
        res.addData(RobotsTxtHelper.getAcceptedLinesData(result.getAcceptedLines(), robotsTxtLines));
        res.addData(RobotsTxtHelper.getAllowData(result.getAreAllowed(), allowInfos, urls));
    }

    @Required
    public void setDispatcherHttpService(DispatcherHttpService dispatcherHttpService) {
        this.dispatcherHttpService = dispatcherHttpService;
    }

    @Required
    public void setRobotsTxtService(RobotsTxtService robotsTxtService) {
        this.robotsTxtService = robotsTxtService;
    }
}
