package ru.yandex.webmaster3.validator.xml;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.apache.http.entity.ContentType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.validator.XmlValidatorSourceType;
import ru.yandex.webmaster3.core.validator.model.ErrorDescription;
import ru.yandex.webmaster3.core.validator.model.ErrorSource;
import ru.yandex.webmaster3.validator.common.wsw.model.WswErrorDescription;
import ru.yandex.webmaster3.validator.xml.ValidateXmlRequest.OutputFormat;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * То же самое, что и {@link ValidateXmlAction}, только без роутерной обвязки в ответе (вызывается старым фронтом)
 * Created by Oleg Bazdyrev on 03/08/2017.
 */
public class ValidateXmlHandler extends AbstractHandler {

    private static final String TARGET = "/validate/oldxml";
    private static final String XML_PARAM = "xml";
    private static final String XML_TYPE_PARAM = "xmlType";
    private static final String XSD_PARAM = "xsd";
    private static final String XSD_TYPE_PARAM = "xsdType";
    private static final String OUTPUT_FORMAT_PARAM = "outputFormat";

    private XmlParser xmlParser;
    private ValidationSourceResolver validationSourceResolver;
    private String applicationTmpFolder;
    private int maxRequestFileSize;

    @Override
    public void handle(String target, Request baseRequest,
                       HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        if (!TARGET.equals(target)) {
            return;
        }
        baseRequest.setHandled(true);
        OutputFormat outputFormat = OutputFormat.XML;
        List<ErrorDescription> errors = new ArrayList<>();
        // собираем аргументы
        try {
            baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT,
                    new MultipartConfigElement(applicationTmpFolder,
                            maxRequestFileSize,
                            maxRequestFileSize,
                            maxRequestFileSize)
            );
            baseRequest.extractParameters();

            if (request.getParameter(OUTPUT_FORMAT_PARAM) != null) {
                outputFormat = OutputFormat.valueOf(request.getParameter(OUTPUT_FORMAT_PARAM));
            }

            InputStream xmlStream;
            XmlValidatorSourceType xmlType = XmlValidatorSourceType.valueOf(request.getParameter(XML_TYPE_PARAM));
            if (xmlType == XmlValidatorSourceType.RESOURCE) {
                Part part = request.getPart("xml");
                Preconditions.checkArgument(part != null, "Xml part is empty");
                xmlStream = part.getInputStream();
            } else {
                String xml = request.getParameter(XML_PARAM);
                Preconditions.checkArgument(!Strings.isNullOrEmpty(xml), "Parameter xml not set");
                try {
                    xmlStream = validationSourceResolver.getSourceInputStreams(xml, xmlType, true)[0];
                } catch (WebmasterException e) {
                    errors.add(new ErrorDescription("Unable to download document", ErrorSource.XML));
                    writeResult(outputFormat, errors, response);
                    return;
                }
            }

            String xsd = request.getParameter(XSD_PARAM);
            Preconditions.checkArgument(!Strings.isNullOrEmpty(xsd), "Parameter xsd not set");
            XmlValidatorSourceType xsdType = XmlValidatorSourceType.valueOf(request.getParameter(XSD_TYPE_PARAM));
            InputStream[] xsdStreams;
            // загружаем xsd
            try {
                xsdStreams = validationSourceResolver.getSourceInputStreams(xsd, xsdType, true);
            } catch (WebmasterException e) {
                errors.add(new ErrorDescription("Unable to download document", ErrorSource.XSD));
                writeResult(outputFormat, errors, response);
                return;
            }

            // валидируем
            errors.addAll(xmlParser.validate(xmlStream, xsdStreams));

            writeResult(outputFormat, errors, response);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().println("Error when validating xml: " + e.getMessage());
            response.flushBuffer();
        }
    }

    private void writeResult(OutputFormat outputFormat, List<ErrorDescription> errors, HttpServletResponse response)
            throws IOException {
        WswErrorDescription wswErrorDescription = new WswErrorDescription(errors);
        final StringBuilder sb = new StringBuilder();
        switch (outputFormat) {
            case XML:
                response.setContentType(ContentType.APPLICATION_XML.getMimeType());
                sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); // на всякий случай
                wswErrorDescription.toXml(sb);
                break;
            case JSON:
                response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
                wswErrorDescription.toJson(sb);
                break;
        }
        response.setStatus(HttpServletResponse.SC_OK);
        response.setCharacterEncoding(Charsets.UTF_8.name());
        response.getWriter().print(sb.toString());
        response.flushBuffer();
    }

    @Required
    public void setXmlParser(XmlParser xmlParser) {
        this.xmlParser = xmlParser;
    }

    @Required
    public void setValidationSourceResolver(ValidationSourceResolver validationSourceResolver) {
        this.validationSourceResolver = validationSourceResolver;
    }

    @Required
    public void setApplicationTmpFolder(String applicationTmpFolder) {
        this.applicationTmpFolder = applicationTmpFolder;
    }

    @Required
    public void setMaxRequestFileSize(int maxRequestFileSize) {
        this.maxRequestFileSize = maxRequestFileSize;
    }
}
