package org.xbib.io.webdav.client.methods;

import static java.net.http.HttpRequest.BodyPublishers.noBody;
import org.w3c.dom.Document;
import org.xbib.io.webdav.api.XMLizable;
import org.xbib.io.webdav.common.DomUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.function.Consumer;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * Base class for request classes defined in this package.
 */
public abstract class AbstractMethod
        implements Method, HttpResponse.BodyHandler<String>, Consumer<HttpRequest.Builder> {

    private static final TransformerFactory FACTORY = TransformerFactory.newInstance();

    @Override
    public HttpResponse.BodySubscriber<String> apply(HttpResponse.ResponseInfo responseInfo) {
        return HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
    }

    /**
     * Check the provided {@link HttpResponse} for successful execution. The default implementation treats all
     * 2xx status codes (<a href="http://webdav.org/specs/rfc7231.html#rfc.section.6.3">RFC 7231, Section 6.3</a>).
     * Implementations can further restrict the accepted range of responses (or even check the response body).
     */
    public boolean succeeded(HttpResponse<?> response) {
        return response.statusCode() >= 200 && response.statusCode() < 300;
    }

    @Override
    public void accept(HttpRequest.Builder httpRequest) {
        httpRequest.header("content-type", "application/xml");
        httpRequest.method(method(), bodyPublisher());
    }

    public HttpRequest.BodyPublisher bodyPublisher() {
        return noBody();
    }

    @Override
    public Map<String, String> headers() {
        return Map.of();
    }

    protected static HttpRequest.BodyPublisher publish(XMLizable xmLizable) {
        try {
            Document document = DomUtil.createDocument();
            document.appendChild(xmLizable.toXml(document));
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Transformer transformer = FACTORY.newTransformer();
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.INDENT, "no");
            transformer.transform(new DOMSource(document), new StreamResult(byteArrayOutputStream));
            return HttpRequest.BodyPublishers.ofByteArray(byteArrayOutputStream.toByteArray());
        } catch (Exception e) {
            throw new UncheckedIOException(new IOException(e));
        }
    }
}
