package com.atlassian.bitbucket.test.rest.server.stub;

import com.atlassian.bitbucket.test.rest.server.stub.mirror.StubServerResponder;
import com.atlassian.fugue.Pair;
import com.confluex.mock.http.MockHttpServer;
import com.confluex.mock.http.matchers.HttpRequestMatcher;
import org.apache.commons.lang3.StringUtils;
import org.mockito.Matchers;

import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

import static com.confluex.mock.http.matchers.HttpMatchers.get;
import static com.confluex.mock.http.matchers.HttpMatchers.post;

/**
 * A stub server that accepts requests
 */
public class StubServer implements Closeable {
    public static final StubServerResponder<StubServer> OK = statusCode(200);

    protected MockHttpServer server;

    public StubServer() {
        server = new MockHttpServer();
    }

    @Override
    public void close() throws IOException {
        stop();
    }

    public void configureDefaultResponders() {
        respondTo(get(Matchers.anyString()), statusCode(200));
        respondTo(post(Matchers.anyString()), statusCode(200));
    }

    public String getBaseUrl() {
        return getBaseUrl(server.getPort());
    }

    public int getPort() {
        return server.getPort();
    }

    public List<Pair<String, String>> getRequests() {
        return server.getRequests().stream().map(request -> Pair.pair(request.getPath(), request.getBody())).collect(Collectors.toList());
    }

    public MockHttpServer getServer() {
        return server;
    }

    public String getUrl(String path) {
        return joinUrlAndPath(getBaseUrl(), path);
    }

    public void stop() {
        server.stop();
    }

    protected void respondTo(HttpRequestMatcher requestMatcher, StubServerResponder responder) {
        // note: we are manually registering our HttpResponder because the fluent interface provided by confluex
        // doesn't allow us to substitute ours for theirs and we can't use theirs because they provide no facility
        // for dynamic responds that react or capture the request values (at least not easily from Java)
        List<HttpRequestMatcher> matchers = server.getHandler().getMatchers();
        matchers.add(0, requestMatcher);
        server.getHandler().getResponders().put(requestMatcher, new StubServerResponderAdapter(this, responder));
    }

    protected boolean waitForPost(String path, long timeout) {
        return server.waitFor(post(path), timeout);
    }

    public static <T extends StubServer> StubServerResponder<T> statusCode(final int statusCode) {
        return (mirror, request) -> new StubResponse(statusCode);
    }

    public static String getBaseUrl(int port) {
        return String.format("http://localhost:%d", port);
    }

    public static String joinUrlAndPath(String baseUrl, String path) {
        return String.format("%s%s", baseUrl, StringUtils.prependIfMissing(path, "/"));
    }
}
