package org.keycloak.testsuite.util.saml;


import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.apache.commons.io.IOUtils;
import org.keycloak.saml.SAMLRequestParser;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.keycloak.testsuite.util.WaitUtils.pause;

public class SamlMessageReceiver implements AutoCloseable {

    private final static Pattern SAML_MESSAGE_PATTER = Pattern.compile(".*SAML(?:Response|Request)=([^&]*).*");

    private final HttpServer server;
    private String message;
    private final String url;

    public SamlMessageReceiver(int port) {
        try {
            InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("localhost"), port);
            server = HttpServer.create(address, 0);
            this.url = "http://" + address.getHostString() + ":" + port ;
        } catch (IOException e) {
            throw new RuntimeException("Cannot create http server", e);
        }
        
        server.createContext("/", new MyHandler());
        server.setExecutor(null);
        server.start();
    }
    
    public String getUrl() {
        return url;
    }
    
    public boolean isMessageReceived() {
        return message != null && !message.trim().isEmpty();
    }

    public String getMessageString() {
        return message;
    }
    
    public SAMLDocumentHolder getSamlDocumentHolder() {
        Matcher m = SAML_MESSAGE_PATTER.matcher(message);
        if (m.find()) {
            try {
                return SAMLRequestParser.parseResponsePostBinding(RedirectBindingUtil.urlDecode(m.group(1)));
            } catch (IOException e) {
                throw new RuntimeException("Cannot parse response " + m.group(1), e);
            }
        }

        return null;
    }

    @Override
    public void close() throws Exception {
        server.stop(0);

        // This is needed because httpClient used for calling, for example, backchannel logout reuses connections when two consequent requests are executed.
        // This causes failures when two consequent tests use SamlMessageReceiver as HttpClient uses connection to HttpServer created for the first test when running the second.
        // This line is needed only in downstream. In upstream this is solved by DoNotReuseConnections strategy in the testsuite, however it is not present in downstream and I didn't want to backport.
        pause(2000);
    }

    private class MyHandler implements HttpHandler {
        public void handle(HttpExchange t) throws IOException {
            t.sendResponseHeaders(200, 0);

            SamlMessageReceiver.this.message = IOUtils.toString(t.getRequestBody(), StandardCharsets.UTF_8.name());
            
            OutputStream os = t.getResponseBody();
            os.close();
        }
    }
}


