/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.technicalconnector.tests.junit.rule;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.URLEncoder;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang3.StringUtils;
import org.junit.rules.ExternalResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.fgov.ehealth.technicalconnector.tests.server.HttpServerStub;
import be.fgov.ehealth.technicalconnector.tests.utils.LoggingUtils;

/**
 * @author EHP
 */
public class HttpServerStubRule extends ExternalResource {

    private static final String SOAP_CONNECTION_FACTORY = "javax.xml.soap.SOAPConnectionFactory";

    private static final Logger LOG = LoggerFactory.getLogger(HttpServerStubRule.class);

    private static final int MAGIC_10 = 10;

    private static final int MAGIC_10000 = 10000;

    private int port;

    private HttpServerStub server;

    private String oldConnectionFactory;

    public HttpServerStubRule() {
        this(getFreePort());
    }

    public HttpServerStubRule(int portNumber) {
        this.port = portNumber;
    }

    @Override
    protected void before() throws Throwable {
        LoggingUtils.bootstrap();
        Validate.isTrue(freePort(port), "Port " + port + " already in use!");
        server = new HttpServerStub(port);
        oldConnectionFactory = System.getProperty(SOAP_CONNECTION_FACTORY);
        System.setProperty(SOAP_CONNECTION_FACTORY, "com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnectionFactory");
    }

    @Override
    protected void after() {
        server.closeAllConnections();
        if (oldConnectionFactory != null) {
            System.setProperty(SOAP_CONNECTION_FACTORY, oldConnectionFactory);
        }
    }

    public String getEchoUrl() {
        return getContentUrl("/echo");
    }

    public String getErrorUrl(int errorCode) {
        return getContentUrl("/error/" + errorCode);
    }

    public String getTimeOutUrl(int period) {
        return getContentUrl("/timeout/" + period);
    }

    public String getContentUrl(String path, String[]... params) {
        StringBuffer url = new StringBuffer();
        url.append("http://localhost:").append(port);
        if (!StringUtils.startsWith(path, "/")) {
            url.append("/");
        }
        url.append(path);
        if (ArrayUtils.isNotEmpty(params)) {
            url.append("?");
            boolean notFirst = false;
            for (String[] param : params) {
                try {
                    if (notFirst) {
                        url.append("&");
                    }
                    url.append(param[0]).append("=").append(URLEncoder.encode(param[1], "UTF-8"));
                    notFirst = true;
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalArgumentException(e);
                }
            }
        }
        return url.toString();
    }

    public String add(String path, String content, String[]... params) {
        server.add(path, content);
        return getContentUrl(path, params);
    }

    public String add(String path, HttpServerStub.HttpAsserter asserter, String[]... params) {
        if (!path.startsWith("/assert/")) {
            path = "/assert/" + path;
        }
        server.add(path, asserter);
        return getContentUrl(path, params);
    }

    private static int getFreePort() {
        int freePort = randomPort();

        while (!freePort(freePort)) {
            freePort = randomPort();
        }
        return freePort;
    }

    private static int randomPort() {
        return (int) (MAGIC_10000 + Math.round(Math.random() * MAGIC_10000));
    }

    private static boolean freePort(int freePort) {
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(freePort);
            freePort = ss.getLocalPort();
            return true;
        } catch (IOException e) {
            LOG.debug("Unable to open port " + freePort, e);
        } finally {
            ConnectorIOUtils.closeQuietly(ss);
        }
        return false;

    }

}